09 - UE中的字符串
本文主要介绍了UE5中常用的字符串类FName,FText和FString的用途和一些简单用法。
字符编码
总的来说,设置字符串文字内容时应使用TEXT()宏包裹,否则字符串文字将以 ANSI 标准编码,导致支持的字符数有限。
在了解字符串类之前,先看看有关字符编码的知识。一般用到的字符编码有:ASCII,ANSI,UTF-8和UTF-16。而 UE中的所有字符串都作为FString或TCHAR数组以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 -> FString:TestHUDString = TestHUDName.ToString();;FName -> FText:TestHUDText = FText::FromName(TestHUDName);,但FName中的内容不适用于FText的自动本地化功能;FString -> FName:TestHUDName = 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() | 此函数为包装,用于以 ETextComparisonLevel 的 Second 值调用 EqualTo |
CompareTo() | 使用ETextComparisonLevel决定要使用的比较规则,返回int32,其中零表示相等,而负值或正值分别表示调用 FText 的序位低于或高于 FText 参数。 |
CompareToCaseIgnored() | 此函数为包装,用于以 ETextComparisonLevel 的 Second 值调用 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.h或FString的API文档。
类型转换
和其他字符串类型的互相转换在上文提过了,接下来看看FString和一般类型的转换。
首先是一般类型 -> FString:
| 变量类型 | 从字符串转换 | 字符串格式 |
|---|---|---|
| float | FString::SanitizeFloat(FloatVariable); | |
| int | FString::FromInt(IntVariable); | |
| bool | InBool ? TEXT("true") : TEXT("false"); | either true or false |
| FVector | VectorVariable.ToString(); | X= Y= Z= |
| FVector2D | Vector2DVariable.ToString(); | X= Y= |
| FRotator | RotatorVariable.ToString(); | P= Y= R= |
| FLinearColor | LinearColorVariable.ToString(); | (R=,G=,B=,A=) |
| UObject | (InObj != NULL) ? InObj->GetName() : FString(TEXT("None")); | UObject`s FName |
对于其他数值转换,可使用带合适参数的 FString::Printf() 函数。
然后是FString -> 其他类型:
从 FString 到 int 和浮点型数字变量,以及到布尔型变量之间也存在转换。
| 变量类型 | 从字符串转换 |
|---|---|
| bool | TestHUDString.ToBool(); |
| int | FCString::Atoi(*TestHUDString); |
| float | FCString::Atof(*TestHUDString); |