你的浏览器不支持canvas

让我们携手共创奇迹!

字符串操作

Date: Author: LBD

本文章采用 知识共享署名-非商业性使用-禁止演绎 4.0 国际许可协议 进行许可。转载请注明来自WindCrazy

这是一篇关于UE中字符串操作的文章

字符串 String

TCHAR 到底是个什么东西?

为什么字符串字面量要包一层 TEXT(“”)宏?

TEXT(““)里字符串出来L”“为什么前面有L


Unicode 编码

  1. “字符编码”是指 可打印“字符” 与 “存储字节序列” 之间的映射关系。(ASCII/GBK/UTF8/UTF16 等)
  2. “Unicode 编码”将这个映射过程拆分为两部分:Codepoint 映射 + 字节转换格式。
  3. “Codepoint 映射”是指 可打印“字符” 与 uint32 数字 之间的映射关系,这个 uint32 数字 称为 Codepoint。
  4. “字节转换格式”是指将 Codepoint 转换为 “存储字节序列”的具体转换方法。
  5. “UTF8”全称是“Unicode Transformation Format - 8 bits”。
  6. 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

字符类型

  1. “存储字节序列” 共有三种:单字节序列、双字节序列、四字节序列。
  2. 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,例如:

  1. TEXT 宏
  2. ByteSwap、FStringView
  3. TStringBuilder/TAnsiStringBuilder: 直接不提供 Wide 版本

对于双字节和四字节,自然存在一个字节序的问题,UE4默认使用小端模式,Unicode编码的文件开头可以有一个BOM前缀,这个前缀就是来记录对应编码的大小端信息:

字符串相关工具

都还没用过,有时间试试

  1. FStringView/FAnsiStringView/FWideStringView: 一段字符串的引用。
  2. TStringBuilder/TAnsiStringBuilder: 避免字符串操作过程创建临时字符串。
  3. FChar/FCharAnsi/FCharWide: IsUpper、IsAlpha、IsWhitespace 等判断字符类型的函数。
  4. FCString/FCStringAnsi/FCStringWide: Strcpy、Strcat、Strcmp、Stricmp 等函数,支持是否区分大小写。
  5. UE::String::BytesToHex/HexToBytes: 形如TEXT("43AF")的字符串与字节互转,一个字节放两个 16 进制数。
  6. LexToString/LexFromString: 通用全局模板,理论上支持任意数据结构与 FString 互转。
  7. FStringFormatter: 通过FString::Format来使用,类似FText::Format的功能,同样支持 Array 或 Map 的格式化参数。
  8. 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基础设施


对于本文内容有问题或建议的小伙伴,欢迎提出问题