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); |