언리얼 공부/이득우의 언리얼 프로그래밍

[공부 내용 정리] 3강 언리얼 C++ 기본 타입과 문자열

dubu0721 2025. 2. 7. 16:59

0. 소스 코드

// Fill out your copyright notice in the Description page of Project Settings.


#include "MyGameInstance.h"

void UMyGameInstance::Init()
{
	Super::Init();

	TCHAR LogCharArray[] = TEXT("Hello Unreal"); // TCHAR 배열로 문자열 저장
	UE_LOG(LogTemp, Log, LogCharArray);

	// FString 에 TCHAR 배열 넣기
	// TArray 라고 하는 언리얼이 제공하는 동적 배열 클래스 방식으로 문자열이 보관됨
	// 동적 배열에서 실제 데이터를 꺼낼 때는 동적 배열이 속하고 있는 내부 자료에 대한 포인터를 가져와서 넘겨줘야 함. 이때 사용하는 것이 포인터 연산자(*)
	// 문자열의 맨 처음 포인터 반환
	FString LogCharString = LogCharArray; 
	UE_LOG(LogTemp, Log, TEXT("%s"), *LogCharString); // 인자로 들어갈 때 포인터 연산자를 꼭 넣어줘야 함.

	const TCHAR* LogCharPtr = *LogCharString;
	TCHAR* LogCharDataPtr = LogCharString.GetCharArray().GetData(); // 실제 TCHAR 의 포인터를 가져옴. 메모리에 직접 접근해서 이용할 수 있음


	// FCString 은 C런타임 수준에서 문자열을 처리하는 클래스
	TCHAR LogCharArrayWithSize[100];
	// 저수준의 복사
	FCString::Strcpy(LogCharArrayWithSize, LogCharString.Len(), *LogCharString);

	if (LogCharString.Contains(TEXT("unreal"), ESearchCase::IgnoreCase)) // IgnoreCase 는 대소문자 구분 안 한다는 의미
	{
		int32 Index = LogCharString.Find(TEXT("unreal"), ESearchCase::IgnoreCase); // unreal 이 시작하는 인댁스
		FString EndString = LogCharString.Mid(Index); // unreal 이 시작하는 위치에서부터 끝까지 잘라줌
		UE_LOG(LogTemp, Log, TEXT("Find Test: %s"), *EndString);
	}

	// Split 함수
	// 이렇게 해야 오류가 안 나네여;;;
	/*
	
	FString Left, Right;
		if (LogCharString.Split(TEXT(" "), &Left, &Right))
		{
			UE_LOG(LogTemp, Log, TEXT("Split Test: %s 와 %s"), *Left, *Right);
		}
		else
		{
			UE_LOG(LogTemp, Warning, TEXT("Failed to split the string."));
		}

	이렇게만 하면 오류남;;
	
	*/
	if (!LogCharString.IsEmpty())
	{
		FString Left, Right;
		if (LogCharString.Split(TEXT(" "), &Left, &Right))
		{
			// 중간에 한글 '와' 가 뒤집힌 물음표로 출력됨
			// 코드를 작성할 때 윈도우에서 한글을 썼기 때문에 CP949 형태의 멀티 바이트 스트링으로 저장돼서 UTF16 을 쓰는 언리얼과 호환이 안 되는 거임
			// 이럴 땐 파일 세이브버튼 누르고 옵션 버튼 눌러서 Save with Encoding 을 누르고 Encoding 을 UTF8 로 바꿔주기~
			UE_LOG(LogTemp, Log, TEXT("Split Test: %s 와 %s"), *Left, *Right);
		}
		else
		{
			UE_LOG(LogTemp, Warning, TEXT("Failed to split the string."));
		}
	}
	else
	{
		UE_LOG(LogTemp, Warning, TEXT("LogCharString is empty!"));
	}


	// 변환 함수
	int32 IntValue = 32;
	float FloatValue = 3.141592;

	FString FloatIntString = FString::Printf(TEXT("Int:%d Float %f"), IntValue, FloatValue);
	FString FloatString = FString::SanitizeFloat(FloatValue);
	FString IntString = FString::FromInt(IntValue);
	UE_LOG(LogTemp, Log, TEXT("%s"), *FloatIntString);
	UE_LOG(LogTemp, Log, TEXT("Int:%s Float:%s"), *IntString, *FloatString);


	// int 스트링을 다시 Int 로 변환해보기
	int32 IntValueFromString = FCString::Atoi(*IntString);
	// float 스트립을 다시 float 으로 변환해보기
	float FloatValueFromString = FCString::Atof(*FloatString);
	FString FloatIntString2 = FString::Printf(TEXT("Int:%d Float %f"), IntValueFromString, FloatValueFromString);
	UE_LOG(LogTemp, Log, TEXT("%s"), *FloatIntString2);


	// FName 의 활용
	// FName: 애셋 관리를 위해 사용되는 문자열 체계
	// -> 대소문자 구분 없음
	// -> 한번 선언되면 바꿀 수 없음
	// -> 가볍고 빠름
	// -> 문자를 표현하는 용도가 아닌 애셋 키를 지정하는 용도로 사용. 빌드시 해시값으로 변환됨.
	// 언리얼은 FName 과 관련된 글로벌 Pool 자료구조를 가지고 있음
	// 값들이 결국 해시 값으로 변환돼서 FName 에는 문자열 정보가 들어가는 것이 아닌 키 값만 들어감.
	FName key1(TEXT("PELVIS"));
	FName key2(TEXT("pelvis"));
	UE_LOG(LogTemp, Log, TEXT("FName 비교 결과 : %s"), key1 == key2 ? TEXT("같음") : TEXT("다름")); // 대소문자를 구분하지 않기 때문에 둘은 같음
	// FName 사용 주의점
	for (int i = 0; i < 10000; ++i) 
	{
		// FName 의 생성자에 문자열을 넣으면 FName 이 하는 일은 문자열을 Key 로 변환한 다음에 긔 Key 가 전역 Pool 에 있는지를 조사하는 작업을 거치게됨
		// 즉, 오버헤드가 발생할 수 있음(for 문에서 생성하면)
		FName SearchInNamePool = FName(TEXT("pelvis"));

		// const 로 한번만 처리하도록 하자
		// 처음 초기화할 때 데이터를 저장하고 그 다음부터는 찾을 일이 없음
		const static FName StaticOnlyOnce(TEXT("pelvis"));
	}


	// FText: 다국어 지원을 위한 문자열 관리 체계
	// -> 일종의 키로 작용함.
	// -> 별도의 문자열 테이블 정보가 추가로 요구됨.
	// -> 게임 빌드 시 자동으로 다양한 국가별 언어로 변환됨.
}

 

1. 소스 코드 & 출력 결과

더보기

1. TCHAR 배열

TCHAR 배열에 문자열을 저장한 후 출력하도록 했다.

TCHAR LogCharArray[] = TEXT("Hello Unreal"); // TCHAR 배열로 문자열 저장
UE_LOG(LogTemp, Log, LogCharArray);

 

 

2. FString

// FString 에 TCHAR 배열 넣기
// TArray 라고 하는 언리얼이 제공하는 동적 배열 클래스 방식으로 문자열이 보관됨
// 동적 배열에서 실제 데이터를 꺼낼 때는 동적 배열이 속하고 있는 내부 자료에 대한 포인터를 가져와서 넘겨줘야 함. 이때 사용하는 것이 포인터 연산자(*)
// 문자열의 맨 처음 포인터 반환
FString LogCharString = LogCharArray; 
UE_LOG(LogTemp, Log, TEXT("%s"), *LogCharString); // 인자로 들어갈 때 포인터 연산자를 꼭 넣어줘야 함.

 

 

 3. 포인터

첫번째 줄은 맨 앞 포인터를 가져오지만 데이터 수정 불가. 두번째 줄은 데이터 수정 가능

const TCHAR* LogCharPtr = *LogCharString;
TCHAR* LogCharDataPtr = LogCharString.GetCharArray().GetData(); // 실제 TCHAR 의 포인터를 가져옴. 메모리에 직접 접근해서 이용할 수 있음

 

 

4. 복사

LogCharArrayWithSize 에 LogCharString 을 복사함.

// FCString 은 C런타임 수준에서 문자열을 처리하는 클래스
TCHAR LogCharArrayWithSize[100];
// 저수준의 복사
FCString::Strcpy(LogCharArrayWithSize, LogCharString.Len(), *LogCharString);

 

 

5. 나누기1

if (LogCharString.Contains(TEXT("unreal"), ESearchCase::IgnoreCase)) // IgnoreCase 는 대소문자 구분 안 한다는 의미
{
	int32 Index = LogCharString.Find(TEXT("unreal"), ESearchCase::IgnoreCase); // unreal 이 시작하는 인댁스
	FString EndString = LogCharString.Mid(Index); // unreal 이 시작하는 위치에서부터 끝까지 잘라줌
	UE_LOG(LogTemp, Log, TEXT("Find Test: %s"), *EndString);
}

 

 

6. 나누기2

if (!LogCharString.IsEmpty())
{
	FString Left, Right;
	if (LogCharString.Split(TEXT(" "), &Left, &Right))
	{
		// 중간에 한글 '와' 가 뒤집힌 물음표로 출력됨
		// 코드를 작성할 때 윈도우에서 한글을 썼기 때문에 CP949 형태의 멀티 바이트 스트링으로 저장돼서 UTF16 을 쓰는 언리얼과 호환이 안 되는 거임
		// 이럴 땐 파일 세이브버튼 누르고 옵션 버튼 눌러서 Save with Encoding 을 누르고 Encoding 을 UTF8 로 바꿔주기~
		UE_LOG(LogTemp, Log, TEXT("Split Test: %s 와 %s"), *Left, *Right);
	}
	else
	{
		UE_LOG(LogTemp, Warning, TEXT("Failed to split the string."));
	}
}
else
{
	UE_LOG(LogTemp, Warning, TEXT("LogCharString is empty!"));
}

 

 

7. 변환 함수

// 변환 함수
int32 IntValue = 32;
float FloatValue = 3.141592;

FString FloatIntString = FString::Printf(TEXT("Int:%d Float %f"), IntValue, FloatValue);
FString FloatString = FString::SanitizeFloat(FloatValue);
FString IntString = FString::FromInt(IntValue);
UE_LOG(LogTemp, Log, TEXT("%s"), *FloatIntString);
UE_LOG(LogTemp, Log, TEXT("Int:%s Float:%s"), *IntString, *FloatString);


// int 스트링을 다시 Int 로 변환해보기
int32 IntValueFromString = FCString::Atoi(*IntString);
// float 스트립을 다시 float 으로 변환해보기
float FloatValueFromString = FCString::Atof(*FloatString);
FString FloatIntString2 = FString::Printf(TEXT("Int:%d Float %f"), IntValueFromString, FloatValueFromString);
UE_LOG(LogTemp, Log, TEXT("%s"), *FloatIntString2);

 

 

 8. FName

// FName 의 활용
// FName: 애셋 관리를 위해 사용되는 문자열 체계
// -> 대소문자 구분 없음
// -> 한번 선언되면 바꿀 수 없음
// -> 가볍고 빠름
// -> 문자를 표현하는 용도가 아닌 애셋 키를 지정하는 용도로 사용. 빌드시 해시값으로 변환됨.
// 언리얼은 FName 과 관련된 글로벌 Pool 자료구조를 가지고 있음
// 값들이 결국 해시 값으로 변환돼서 FName 에는 문자열 정보가 들어가는 것이 아닌 키 값만 들어감.
FName key1(TEXT("PELVIS"));
FName key2(TEXT("pelvis"));
UE_LOG(LogTemp, Log, TEXT("FName 비교 결과 : %s"), key1 == key2 ? TEXT("같음") : TEXT("다름")); // 대소문자를 구분하지 않기 때문에 둘은 같음
// FName 사용 주의점
for (int i = 0; i < 10000; ++i) 
{
	// FName 의 생성자에 문자열을 넣으면 FName 이 하는 일은 문자열을 Key 로 변환한 다음에 긔 Key 가 전역 Pool 에 있는지를 조사하는 작업을 거치게됨
	// 즉, 오버헤드가 발생할 수 있음(for 문에서 생성하면)
	FName SearchInNamePool = FName(TEXT("pelvis"));

	// const 로 한번만 처리하도록 하자
	// 처음 초기화할 때 데이터를 저장하고 그 다음부터는 찾을 일이 없음
	const static FName StaticOnlyOnce(TEXT("pelvis"));
}