跳到主要内容

09 - UE中的字符串

本文主要介绍了UE5中常用的字符串类FNameFTextFString的用途和一些简单用法。

字符编码

TL;DR

总的来说,设置字符串文字内容时应使用TEXT()宏包裹,否则字符串文字将以 ANSI 标准编码,导致支持的字符数有限。

在了解字符串类之前,先看看有关字符编码的知识。一般用到的字符编码有:ASCIIANSIUTF-8UTF-16。而 UE中的所有字符串都作为FStringTCHAR数组以UTF-16的格式存储在内存中,并且大多数代码假设2个字节等于1个码点。

此外,UE提供了一些工具将字符串转换为各种编码,或将各种编码转换为字符串。不过官方文档提供的宏已经被标识弃用,UE5时代推荐用StringCast<>()代替:

被标识弃用的宏StringCast<>()版本
TCHAR_TO_ANSI(str)StringCast<ANSICHAR>(PtrToTChar)
ANSI_TO_TCHAR(str)StringCast<TCHAR>(PtrToAnsiChar)
TCHAR_TO_UTF8(str)StringCast<UTF8CHAR>(PtrToTChar)
UTF8_TO_TCHAR(str)StringCast<TCHAR>(PtrToUTF8Char)
...详见Source/Runtime/Core/Public/Containers/StringConv.h

如果非要使用宏,需要注意:编码转换前后字符串所占的字节数也可能会改变,因此不推荐自己手动计算字节数,而是通过填充数组的方式解决:

FString Str = TEXT("str......");

// TCHAR -> Utf-8
FTCHARToUTF8 Convert(*Str);
TArray<uint8> Data;
Data.Append((uint8*)Convert.Get(), Convert.Length());

// Utf-8 -> TCHAR
FUTF8ToTCHAR BackConvert((const ANSICHAR*)Data.GetData(), Data.Num());
FString TCharText(BackConvert.Length(), BackConvert.Get());

此外,在使用TCHAR_TO_ANSI宏时要注意长度不能超过128个字符,这和临时内部对象缓冲区有关,详见官方论坛

由于使用宏转换编码会有上述两种隐患,因此还是推荐使用更新的StringCast<>()(UE5.1版本及更新的):

FString Str = TEXT("str......");

// TCHAR -> UTF-8
auto UTF8String = StringCast<UTF8CHAR>(*Str);
TArray<uint8> Data;
Data.SetNumUninitialized(UTF8String.Length());
FMemory::Memcpy(Data.GetData(), UTF8String.Get(), UTF8String.Length());

// UTF-8 -> TCHAR
auto TCharString = StringCast<TCHAR>((const UTF8CHAR*)Data.GetData(), Data.Num());
FString Restored(TCharString.Get(), TCharString.Length());

FName

FName是一种用于高效字符串处理的轻量级类型。UE会维护一个全局唯一的字符串表,FName存储一个实例编号和指向对应字符串的索引引用,以便快速查找和访问。FName在表示对象名称、标识符和其他常被用于比较的字符串时尤其有用,例如骨骼网格体中的Socket Name

需要注意的是,FName不区分大小写,不可变且无法被操作

备注

如果给FName启用WITH_CASE_PRESERVING_NAME宏,那么就会区分大小写。

创建

创建一个FName很简单:

FName TestHUDName = FName(TEXT("MyTestFName"));

常用操作

对比

可直接用==对比两个FName,它不执行字符串的对比,而是直接对比索引中的数值。也能用FName::Compare()对比两个FName,如果小于则返回负数,等于则返回0,大于则返回正数。

搜索名称表

如果要确认某个FName是否在全局名称表中(但不希望将其添加进去),可在FName的构造函数中补充一个搜索类型:

if (FName(TEXT("xxx"), FNAME_Find) != NAME_None)
{
// ...
}

类型转换

三个字符串类间可进行类型转换,但一些转换是不可靠的:

  • FName -> FStringTestHUDString = TestHUDName.ToString();
  • FName -> FTextTestHUDText = FText::FromName(TestHUDName);,但FName中的内容不适用于FText的自动本地化功能;
  • FString -> FNameTestHUDName = FName(*TestHUDString);,但转换会有损耗,因为FName不区分大小写;
  • FText -> FName:需要先转化为FString,再转换为FName

FText

FText主要用于文本本地化,提供下列功能:

  • 创建本地化的文本文字;
  • 设置文本格式(根据占位符模式生成文本);
  • 根据数字生成文本;
  • 根据日期时间生成文本;
  • 生成衍生文本,例如将文本设为大写或小写;
备注

有关文本本地化的更多内容,详见这里

创建

可使用FText::GetEmpty()FText()来创建空白的FText(),还能通过宏的LOCTEXT系列使用C++创建文本文字值:

文本文字值宏说明
NSLOCTEXT通过定义命名空间、密钥和源字符串创建本地化的文本片段。
LOCTEXT通过定义密钥和源字符串创建本地化的文本片段,并使用LOCTEXT_NAMESPACE定义命名空间。
// 定义要与LOCTEXT一起使用的命名空间
// 这只在单个文件中有效,并且必须在文件结束前取消定义
#define LOCTEXT_NAMESPACE "MyNamespace"
// 创建文本文字值
constFTextHelloWorld= NSLOCTEXT("MyOtherNamespace","HelloWorld","Hello World!")
constFTextGoodbyeWorld= LOCTEXT("GoodbyeWorld","Goodbye World!")
// 在文件结束前取消定义命名空间
#undef LOCTEXT_NAMESPACE

常用操作

对比

FText数据比简单字符串复杂,因此不支持重载运算符比较,应使用它提供的下列比较函数:

函数描述
EqualTo()使用ETextComparisonLevel决定比较规则,返回bool
EqualToCaseIgnored()此函数为包装,用于以 ETextComparisonLevelSecond 值调用 EqualTo
CompareTo()使用ETextComparisonLevel决定要使用的比较规则,返回int32,其中零表示相等,而负值或正值分别表示调用 FText 的序位低于或高于 FText 参数。
CompareToCaseIgnored()此函数为包装,用于以 ETextComparisonLevelSecond 值调用 CompareTo

UI显示

Slate和UMG使用内置控件的 FText 属性或参数,此类内置控件显示或控制面向用户文本。建议使用用户构建的自定义控件的 FText

要利用带Canvas的HUD系统显示 FText,新建 FCanvasTextItem 并将其 Text 变量设为要显示的文本,如下范例代码所示:

// Create a new FCanvasTextItem instance to contain the text.
FCanvasTextItem TextItem(FVector2D::ZeroVector, TestHUDText, BigFont, FLinearColor::Black);
// Add the text into the FCanvasTextItem.
TextItem.Text = FText::Format(LOCTEXT("ExampleFText", "You currently have {0} health left."), CurrentHealth);
// Draw the text to the screen with FCanvas::DrawItem.
Canvas->DrawItem(TextItem, 10.0f, 10.0f);

文本生成

FText提供若干函数让开发者进行文本生成,方便本地化操作:

数值型

函数说明
FText::AsNumber将UE支持的任何数值类型转换为用户友好的文本表示形式(1234.5变为1,234.5)。
FText::AsPercent将浮点数或双精度浮点数转换为百分比文本表示形式(0.2变为20%)。
FText::AsMemory将值(以字节为单位)转换为用户友好的内存表示形式(1234变成1.2 KiB)。
FText::AsCurrencyBase将采用基本表示形式的货币值转换为用户友好的货币表示形式(USD 123450转换为$1,234.50)。

表中的大多数函数通过一个可选的FNumberFormattingOptions来控制输出。

时间型

函数说明
FText::AsDate将FDateTime值转换为用户友好的日期表示形式。
FText::AsTime将FDateTime值转换为用户友好的时间表示形式。
FText::AsDateTime将FDateTime值转换为用户友好的日期和时间表示形式。
FText::AsTimespan将FTimespan值转换为用户友好的时间增量表示形式(采用小时、分钟和秒)。

表中的大多数函数通过EDataTimeStyle控制输出。

变换型

函数说明
FText::ToLower以符合unicode标准的方式将FText实例转换为其小写形式。
FText::ToUpper以符合unicode标准的方式将FText实例转换为其大写形式。

类型转换

FText可和FString相互转换:

  • AsCultureInvariant():以现有FString创建非本地化且语言不变的FText实例;
  • FromString():通过现有的FString创建一个非本地化的FText实例;
  • FText::ToString():将FText转换为FString

此外,可通过FText::FromName()将现有FName创建非本地化的FText实例。

FString

FString是较常用的字符串类型,它可以搜索,修改并且与其他字符串比较,相对其余两个字符串类开销更大一些。

创建

使用以下语法创建FString变量:

FString TestHUDString = FString(TEXT("This is my test FString."));

此外,还能通过+=+串联字符串;以及通过FString::Printf()构建字符串:

FString AShooterHUD::GetTimeString(float TimeSeconds)
{
// only minutes and seconds are relevant
const int32 TotalSeconds = FMath::Max(0, FMath::TruncToInt(TimeSeconds) % 3600);
const int32 NumMinutes = TotalSeconds / 60;
const int32 NumSeconds = TotalSeconds % 60;

const FString TimeDesc = FString::Printf(TEXT("%02d:%02d"), NumMinutes, NumSeconds);
return TimeDesc;
}

常用操作

对比

可用==FString::Equals()比较两个FString,后者可通过ESearchCase枚举决定对比是否忽略大小写:

// 忽略大小写:   ESearchCase::IgnoreCase
// 不忽略大小写: ESearchCase::CaseSensitive
TestHUDString.Equals(TEXT("Test"), ESearchCase::CaseSensitive);

搜索

可使用FString::Contains()查找子串,返回bool值。通过ESearchCase枚举指定搜索是否忽略大小写,通过ESearchDir枚举指定搜索的方向。默认忽略大小写,从开始执行搜索。

TestHUDString.Contains(TEXT("Test"), ESearchCase::CaseSensitive, ESearchDir::FromEnd);

还可使用FString::Find()查找,返回找到的第一个子字符串实例的索引(找不到返回-1)。除了可设定大小写敏感和搜搜索方向外,还能指定搜索索引的位置。

TestHUDString.Find(TEXT("test"), ESearchCase::CaseSensitive, ESearchDir::FromEnd, 10);

其他操作

FString还提供了许多操作函数,例如:

  • 通过Left()Right()Mid()在找到的子字符串的位置将一个字符串分为两个字符串;
  • 通过Split()拆分字符串;
  • 通过REplace()替换字符串;
  • 通过ToUpper()ToLower()进行大小写转换;
  • 等等;

详见UnrealString.hFString的API文档。

类型转换

和其他字符串类型的互相转换在上文提过了,接下来看看FString和一般类型的转换。

首先是一般类型 -> FString

变量类型从字符串转换字符串格式
floatFString::SanitizeFloat(FloatVariable);
intFString::FromInt(IntVariable);
boolInBool ? TEXT("true") : TEXT("false");either true or false
FVectorVectorVariable.ToString();X= Y= Z=
FVector2DVector2DVariable.ToString();X= Y=
FRotatorRotatorVariable.ToString();P= Y= R=
FLinearColorLinearColorVariable.ToString();(R=,G=,B=,A=)
UObject(InObj != NULL) ? InObj->GetName() : FString(TEXT("None"));UObject`s FName

对于其他数值转换,可使用带合适参数的 FString::Printf() 函数。

然后是FString -> 其他类型

从 FString 到 int 和浮点型数字变量,以及到布尔型变量之间也存在转换。

变量类型从字符串转换
boolTestHUDString.ToBool();
intFCString::Atoi(*TestHUDString);
floatFCString::Atof(*TestHUDString);

参考资料