【问题标题】:Delphi 2010 system.pos search utf8stringDelphi 2010 system.pos 搜索 utf8string
【发布时间】:2017-08-04 09:22:35
【问题描述】:

我的问题是关于 delphi 2010 system.pos

我想知道为什么:

const
ThisStr : UTF8String = 'abcā—46°40’こ';  // — is #$E28094,  ’ is #$E28099

i := Pos('°', ThisStr);   // not found (i = 12 instead of 8)
i := Pos('—', ThisStr);   // i = 0 instead of 5
i := Pos('’', ThisStr);   // i = 0 instead of 11

如果我将 char 转换为字符串,所有答案都是正确的。

i := Pos(string('—'), ThisStr) // i = 5

NB1:

i := Pos('こ', ThisStr); // i = 12
i := Pos('ā', ThisStr); // i = 4

给出正确答案,不需要从字符转换为字符串。

NB2: sysutils.ansipos、strutils.ansipos、strutils.posex
在所有情况下都给出正确答案,无需将字符转换为字符串。

【问题讨论】:

  • 自己调试。找出使用了Pos 的哪个重载。找出两个输入字符串是如何编码的。
  • 'こ' 和 'ā' 从 UTF8String 隐式转换为字符串。但我不明白为什么'°','-'和'''不是。他们已经是 unicodestrings 了吗?我不希望它们成为原始字节字符串!如果是这样,为什么其他两个不是?对我来说,其中五个是 UTF8Strings。
  • 这对我来说没有多大意义。我想知道你为什么要混合使用 UTF8 和 UTF16。 Delphi 的内部库与 UTF16 一起工作。你为什么要让自己的生活变得艰难。要么切换到 UTF16,要么找一些 UTF8 库。
  • 感谢您的帮助。我同意你的看法。这个测试的原因是因为 TPerlRegEx for Delphi 2010 与 UTF8Strings 一起工作,我遇到了这个问题。

标签: delphi


【解决方案1】:

ThisStrUTF8String,而您的其他 System.Pos() 输入是无类型文字。这是一个需要注意的重要事实,因为它会影响编译器调用System.Pos() 的方式。

在 D2010 中,System 单元具有 System.Pos() 三个重载:

function Pos(const substr, str: UnicodeString): Integer; overload;
function Pos(const substr, str: RawByteString): Integer; overload;
function Pos(const substr, str: WideString): Integer; overload;

在不进行任何转换的情况下调用 System.Pos() 时,您的输入匹配最接近 RawByteString 重载,因为它是唯一不需要对您的输入值进行任何运行时转换的重载。

无类型文字的编译字符编码基于它使用的上下文。文字'abcā—46°40’こ' 被分配给UTF8String,因此它在编译时是UTF-8 编码的。 UTF8String 可以按原样传递给 RawByteString,这意味着您的其他文字被分配给 RawByteString,因此它们将由编译器以 ANSI 编码,而不是 UTF-8。

您的 UTF-8 字符串中有 12 个 Unicode 字符。以 UTF-8 编码时,它有 20 个AnsiChar 元素:

a = $61
b = $62
c = $63
ā = $C4 $81
— = $E2 $80 $94
4 = $34
6 = $36
° = $C2 $B0
4 = $34
0 = $30
’ = $E2 $80 $99
こ = $E3 $81 $93 

另一方面,当以 ANSI 编码时,每个字符文字将只有 1 个 AnsiChar(它们的值将取决于您系统的默认语言环境,因此您的结果可能与我的不同):

° = $B0 
— = $97 
’ = $92
こ = $3F (unless your system locale supports this character! mine doesn't)
ā = $61

即使您将源文件编码更改为 UTF-8,字符文字仍将以 ANSI 编码,而不是 UTF-8。

所以:

  • Pos('°', ThisStr) 正在搜索 $B0,它位于元素 12
  • Pos('—', ThisStr) 正在搜索 $97,但没有找到
  • Pos('’', ThisStr) 正在搜索 $92,但没有找到
  • Pos('こ', ThisStr) 正在搜索 $3F,但没有找到
  • Pos('ā', ThisStr) 正在搜索 $61,它位于元素 1

正在搜索的字符与UTF8String 不匹配,这就是它们未按预期找到的原因。

通过将System.Pos() 的第一个参数强制转换为string,您将强制编译器使用UnicodeString 重载而不是RawByteString 重载。因此,UTF8String 将在运行时转换为 UnicodeString,并将字符文字分配给 UnicodeString,因此它们将在编译时以 UTF-16 编码。

转换后的UTF8String->UnicodeString将包含12个WideChar元素:

a = $0061
b = $0062
c = $0063
ā = $0101
— = $2014
4 = $0034
6 = $0036
° = $00B0
4 = $0034
0 = $0030
’ = $2019
こ = $3053

当您的字符文字以 UTF-16 编码时,每个字符文字将有 1 个 WideChar

° = $00B0
— = $2014
’ = $2019
こ = $3053 
ā = $0101 

所以:

  • Pos(string('°'), string(ThisStr)) 正在搜索 $00B0,它位于元素 8
  • Pos(string('—'), string(ThisStr)) 正在搜索 $2014,它位于元素 5
  • Pos(string('’'), string(ThisStr)) 正在搜索 $2019,它位于元素 11
  • Pos(string('こ'), string(ThisStr)) 正在搜索 $3053,它位于元素 12
  • Pos(string('ā'), string(ThisStr)) 正在搜索 $0101,它位于元素 4

正在搜索的字符与转换后的UTF8String->UnicodeString 字符串匹配,这就是它们按预期找到的原因。

SysUtils.AnsiPos()StrUtils.PosEx() 没有重载,它们只接受UnicodeString 作为输入,因此它们与调用System.Pos()UnicodeString 重载具有相同的结果。如果在uses 子句中包含AnsiStrings 单元,它会为AnsiString 添加AnsiPos() 的重载,这将得到与我上面描述的完全不同的结果。

上下文和字符编码很重要!

【讨论】:

  • 非常感谢。我得到了 100% 的您如此清晰和全面的解释(我并不感到惊讶,因为我阅读了您的许多文章)。
猜你喜欢
  • 2021-03-10
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-01-09
  • 1970-01-01
  • 2011-01-30
  • 1970-01-01
  • 2017-07-11
相关资源
最近更新 更多