这是一篇关于UE中字符串操作的文章
字符串 String
TCHAR 到底是个什么东西?
为什么字符串字面量要包一层 TEXT(“”)宏?
TEXT(““)里字符串出来L”“为什么前面有L
Unicode 编码
- “字符编码”是指 可打印“字符” 与 “存储字节序列” 之间的映射关系。(ASCII/GBK/UTF8/UTF16 等)
- “Unicode 编码”将这个映射过程拆分为两部分:Codepoint 映射 + 字节转换格式。
- “Codepoint 映射”是指 可打印“字符” 与 uint32 数字 之间的映射关系,这个 uint32 数字 称为 Codepoint。
- “字节转换格式”是指将 Codepoint 转换为 “存储字节序列”的具体转换方法。
- “UTF8”全称是“Unicode Transformation Format - 8 bits”。
- UTF8 这个名称包含两个信息,一是使用 Codepoint 映射,二是定义了转换方法。
“A”和“好”这两个字符在各种编码下的数值表示如下:
| 字符 | ASCII | Unicode 的 Codepoint | UTF8 编码字节 | UTF16 编码 | GBK 的 Codepoint | GBK 编码字节 |
|---|---|---|---|---|---|---|
| A | 65 | 65 | 0x41 | 0x0041 | 65 | 0x41 |
| 好 | 无 | 22909 | 0xE5 0xA5 0xBD | 0x597D | 50106 | 0xBA 0xC3 |
字符类型
- “存储字节序列” 共有三种:单字节序列、双字节序列、四字节序列。
- UE 中: ANSICHAR 是 char,WIDECHAR 是 双字节版本,TCHAR 默认定义是 WIDECHAR。
三种字节序列在各个平台对应的数据类型如下:(最后一行为对应的编码格式)
| 平台 | 单字节 | 双字节 | 四字节 |
|---|---|---|---|
| Windows: | char | wchar_t | char32_t |
| Linux: | char | char16_t | wchar_t |
| MacOSX: | char | char16_t | wchar_t |
| 字符编码 | ASCII/UTF8 | UTF16 | UTF32 |
几种数据类型对应的字面量前缀如下:
| 编码 | 类型 | 字面量前缀 | 代码示例 |
|---|---|---|---|
| ASCII | char | 无 | “Hello” |
| UTF8 | char | u8 | u8”World” |
| UTF16 | char16_t | u | u”Nice” |
| UTF32 | char32_t | U | U”Good” |
| UTF16/UTF32 | wchar_t | L | L”Stupid” |
由于双字节版本在不同平台对应不同数据类型,这导致需要不同的字面量前缀,因此UE4需要使用TEXT宏包装一下字符串字面量:
// If we don't have a platform-specific define for the TEXT macro, define it now.
#if !defined(TEXT) && !UE_BUILD_DOCS
#if PLATFORM_TCHAR_IS_CHAR16
#define TEXT_PASTE(x) u ## x
#else
#define TEXT_PASTE(x) L ## x
#endif
#define TEXT(x) TEXT_PASTE(x)
#endif
看 UE 源码注释中说明设计之初是 TCHAR 可以在 ANSICHAR 和 WIDECHAR 之间任意切换,然而实际引擎中某些地方默认 TCHAR 是 2 字节,不再支持切 ANSICHAR,例如:
- TEXT 宏
- ByteSwap、FStringView
- TStringBuilder/TAnsiStringBuilder: 直接不提供 Wide 版本
对于双字节和四字节,自然存在一个字节序的问题,UE4默认使用小端模式,Unicode编码的文件开头可以有一个BOM前缀,这个前缀就是来记录对应编码的大小端信息:

字符串相关工具
都还没用过,有时间试试
- FStringView/FAnsiStringView/FWideStringView: 一段字符串的引用。
- TStringBuilder/TAnsiStringBuilder: 避免字符串操作过程创建临时字符串。
- FChar/FCharAnsi/FCharWide: IsUpper、IsAlpha、IsWhitespace 等判断字符类型的函数。
- FCString/FCStringAnsi/FCStringWide: Strcpy、Strcat、Strcmp、Stricmp 等函数,支持是否区分大小写。
- UE::String::BytesToHex/HexToBytes: 形如
TEXT("43AF")的字符串与字节互转,一个字节放两个 16 进制数。 - LexToString/LexFromString: 通用全局模板,理论上支持任意数据结构与 FString 互转。
- FStringFormatter: 通过
FString::Format来使用,类似FText::Format的功能,同样支持 Array 或 Map 的格式化参数。 - ExpressionParser: 通用的表达式解析框架,FStringFormatter、FBasicMathExpressionEvaluator、FUnitConversion、FFrameRateParser 等解析功能都使用这个框架。
操作
1,获取子字符串开始的下标位置
方法一
constexpr FAsciiSet Delimiters = FAsciiSet(".") + (char)SUBOBJECT_DELIMITER_CHAR;
FString InOutName = "taaa/bbb/ccc/ttt/ddd.ttt";
const TCHAR* DelimiterOrEnd = FAsciiSet::FindFirstOrEnd(*InOutName, Delimiters);//子串为 .ttt
const int32 DotIndex = static_cast<int32>(DelimiterOrEnd - *InOutName);//.的下标为20
但是这种不是所有字符都可以这样操作的,原因在此:
template<class CharType>
static constexpr const CharType* FindFirstOrEnd(const CharType* Str, FAsciiSet Set)
{
for (FAsciiSet SetOrNil(Set.LoMask | NilMask, Set.HiMask); !SetOrNil.Test(*Str); ++Str);
return Str;
}
可以看到在for循环中对传入的字符串指针进行移位,并返回移位后的字符串指针这样在内存中的差位才是正确的下标,因此如果我们有这样操作得出的两个字符串指针,就可以通过差值计算出子串始位下标
二、TStringBuilder
/** An extendable string builder for TCHAR. */
template <int32 BufferSize> using TStringBuilder = TStringBuilderWithBuffer<TCHAR, BufferSize>;
这是一个用来构建TCHAR类型的字符串的类,可以将任意字符类型的字符串传入并且有各个平台的字符类型转换,看下面这个例子
TStringBuilder<FName::StringBufferSize> PartialName;
PartialName.Append(*InOutName, DotIndex);//FString InOutName = "t/ddd.ttt";int32 DotIndex;
传入DotIndex长度的InOutName串
参考链接
1,UE4基础设施