언리얼C++ 문자열

이승한·2024년 1월 1일
0

Unreal5

목록 보기
2/11

FString 문자열 처리

  1. TEXT("HELLO")을 선언하면 TCHAR 배열로 만들어 진다.
  2. TCHAR배열을 FString으로 집어넣는 순간, TArray라 하는 언리얼 엔진이 제공하는 동적 배열 클래스 방식으로 HELLO 문자열을 보관
  3. 이 동적배열에서 다시 실제 데이터를 꺼냈을 때는(FString -> TCHAR),동적 배열이 속하고 있는 내부 자료에 대한 포인터를 가져와서 넘겨준다.(디 레퍼런싱 -> 포인터 변수 앞에 *붙이는 것 포인터연산자)
  4. 그래서 포인터연산자를 사용하게 되면, FString에 포함하고 있는 동적배열,TArray에 포함하고있는 데이터의 첫번째 인자 포인터를 반환 해준다.

FString에서는 자체적으로 다양한 기능을 제공 해주는데, 데이터를 자르거나 붙이거나 새로운 방식으로 불러 들이거나, 다양한 연산이 가능하다.

또한, FString이 제공하는 함수에서 내부적으로 FCString이라는 Wrapper Class가 있는데,
C라이브러리에서 제공하는 기본적인 스트링 관련 함수를 가지고 있다.

그래서, FString은 데이터를 보관하지만, 실제 문자열을 찾거나 자르거나 이런 처리들은 FCString을 통해 처리 한다.


FString 활용

  • 다른 타입에서 FString 변환
  • FString::Printf
  • FString::SanitizeFloat
  • FString::FromInt
  • C런타임 수준에서 문자열을 처리하는 클래스 FCString
  • 문자열 찾는 strstr 사용
  • 그래서 TCHAR 배열을 -> FString -> FCString 변환 후 사용
  • FString에서 다른 타입으로 변환 (안전하지않음 주의)
  • FCString::Atoi
  • FCString::Atof

코드와 주석 참조

#include "MyGameInstance.h"
void UMyGameInstance::Init()
{
	Super::Init();
    
    static const TCHAR LogCharArray[] = TEXT("Hello Unreal");
    UE_LOG(LogTemp, Log, LogCharArray);
    //출력 : Hello Unreal
    
    //%s와 대응될 때는 TCHAR의 포인터 어레이를 반환해 줘야 하는데 FString을 그대로 쓰면, TCHAR 포인터가 반환 되지않는다.
	//그래서 * 을 붙여서 포인터 연산자를 지정해주면 된다.
    FString LogCharString = LogCharArray;
    UE_LOG(LogTemp, Log, *LogCharString);
    //출력 : Hello Unreal
    
    //기본적으로 FString을 다시 TCHAR로 변환하고 싶다하면 const형태로 바꾸지않는걸로 받아온다.
	const TCHAR* LongCharPtr = *LogCharString;
	//근데 FString을 직접 포인터로 받아와서 값을 바꾸고 싶다면 FString이 관리하고있는 	TArray에 첫 인자인 포인터를 가져와야 한다.
	TCHAR* LogCharDataPtr = LogCharString.GetCharArray().GetData();
    
    //만약에 다시 배열로 가져오고 싶다면
	TCHAR LogCharArrayWithSize[100];
	FCString::Strcpy(LogCharArrayWithSize, LogCharString.Len(), *LogCharString);
    
    //Hello Unreal 에서 Unreal만 추출
    if (LogCharString.Contains(TEXT("unreal"), ESearchCase::IgnoreCase)) // 대소문자 상관없이 포함하고있는지
	{
		int32 Index = LogCharString.Find(TEXT("unreal"), ESearchCase::IgnoreCase);
		FString EndString = LogCharString.Mid(Index);
		UE_LOG(LogTemp, Log, TEXT("Find Test: %s"), *EndString);
        // 출력 : Find Test: Unreal
	}
    
    //공백을 기준으로 왼쪽 오른쪽으로 자르기 Left = Hello , Right = Unreal 저장
    FString Left, Right;
	if (LogCharString.Split(TEXT(" "), &Left, &Right)) 
	{
		UE_LOG(LogTemp, Log, TEXT("Split Test: %s 와 %s"), *Left, *Right);
        // 출력 : Split Test: Hello 와 Unreal
        // 여기서 와 라는 한글이 제대로 출력되지않고 이상한 기호로 나타난다면,
        // visual studio에서 save as 후 Save 밑에 Save with Encoding 에서
        //Uncode(UTF-8 with signature) 로 변경해서 저장해주면 제대로 나타날 것이다.
	}
    
    //int,float -> FString으로 변환
    int32 IntValue = 32;
	float FloatValue = 3.141592;
	FString FloatIntString = FString::Printf(TEXT("Int:%d Float:%f"), IntValue, FloatValue);
	//Float단일 변환
	FString FloatString = FString::SanitizeFloat(FloatValue);
	//int단일 변환
	FString IntString = FString::FromInt(IntValue);
	
	UE_LOG(LogTemp, Log, TEXT("%s"), *FloatIntString);
    //출력 :Int:32 Float:3.141592
	UE_LOG(LogTemp, Log, TEXT("Int:%s Float:%s"), *IntString,*FloatString);
    //출력 :Int:32 Float:3.141592
    
    //FString -> int,float으로 변환
    int32 IntValueFromString = FCString::Atoi(*IntString);
	float FloatValueFromString = FCString::Atof(*FloatString);
	FString FloatIntString2 = FString::Printf(TEXT("Int:%d Float:%f"), IntValueFromString, FloatValueFromString);
	UE_LOG(LogTemp, Log, TEXT("%s"), *FloatIntString2);
    //출력 :Int:32 Float:3.141592
}

FName

  • FString으로 문자열 관리를 하는데 FName으로 변환하여 다양한 용도로 사용 할 수있다.
  • 에셋관리를 위해 사용되는 문자열 체계
  • 대소문자 구별 없음
  • 가볍고 빠름
  • 한번 선언되면 바꿀 수 없음
  • 문자를 표현하는 용도가 아닌 애셋 키를 지정하는 용도로 사용
  • 빌드시 해시값으로 변환됨

-> 우리가 어떤 asset을 빠르게 찾고 싶을 때, string으로 저장하여 찾게 되면 비교하여 찾으니 오래 걸린다. 그래서, 문자열을 사용하지만 내부적으로 해시값을 사용해서 바로바로 찾을 수 있게 만들어 준다.

-> 그래서 Key,Value 쌍으로 빠르게 우리가 asset을 찾을 수 있게 한다.

-> Key로 변환되기에 int로 저장된다.

-> FString -> FName 으로 변환할때 대소문자를 구분하지 않기에 다시 FName-> FString으로 변환하게 되면 대소문자를 구분하지 않기에 데이터가 손실 된다.

FName은 FNamePool 이라하는 싱글톤이로 되어 있는 글로벌 Pool 자료구조를 가지고 있다.

  • 문자열이 들어오면 해시 값을 추출해 키를 생성하여 FName에 보관
  • FName 값에 저장된 값을 사용해 전역Pool에서 원하는 자료를 검색해서 반환
  • 문자 정보는 대소문자를 구분하지 않고 저장(IgnoreCase)

FName의 형성

  • 생성자에 문자열 정보를 넣으면 풀을 조사해 적당한 키로 변환하는 작업 수반
    - Find or Add (실제로 FName으로 할수 있는 행동 -> 찾거나 추가하거나)

흐름
1. HELLO 라는 문자열 저장
2. FName에서는 문자열을 어딘가 따로 저장을 하고 문자열을 변환한 해시 값과 key값이 저장
3. 이 key값을 사용해서 Pool에 있는지 없는지 빠르게 검출
4. Pool에 있으면 검출 후 우리가 원하는 데이터를 가져오는 형태로 구성

- 그래서 실제로는 key값만 들어가 있기 때문에 용량이 굉장히 작다

FName key1(TEXT("GHOSTORY"));
FName key2(TEXT("ghostory"));
UE_LOG(LogTemp, Log,TEXT("FName 비교 결과 : %s"), key1 == key2 ? TEXT("같음") : TEXT("다름"));
//출력 : FName 비교 결과 : 같음

//빈번하게 호출 될때를 가정
for(int i=0; i< 10000; ++i)
{
	//이렇게 생성자에 문자열을 넣으면, FName이 문자열을 key로 변환한 다음에
    //이 key가 전역 Pool에 있는지를 조사하는 작업을 거친다.
    //그래서 이렇게 구현을 하면 빈번하게 씬마다 호출된다 가정하면 오버헤드가 일어난다.
	FName SearchInNamePool = FName(TEXT("ghostory"));
    
    //이렇게 한번만 처리하게 해주는게 좋다.
    //처음 초기화 할때 데이터를 저장하고 local static 으로 선언했으니 이걸 찾을 일이 없다.
    //이 자체를 쓰면 되기 때문에 효과적이다.
    const static FName StaticOnlyOnce(TEXT("ghostory"));
}

0개의 댓글