sse*_*sse 9 delphi unicode surrogate-pairs
我试图更好地理解Delphi中的代理对和Unicode实现.
如果我在Delphi中对Unicode字符串S:='ĤàVe'调用length(),我会回来,8.
这是因为各个字符[Ĥ],[à],[V]和[e]的长度分别为2,3,2和1.这是因为Ĥ有代理人,有两个额外的代理人,V有代理人,e没有代理人.
如果我想返回字符串中的第二个元素,包括所有代理,[à],我该怎么做?我知道我需要对各个字节进行某种测试.我使用例程运行了一些测试
function GetFirstCodepointSize(const S: UTF8String): Integer;
Run Code Online (Sandbox Code Playgroud)
在这个SO问题中引用.
但得到了一些不寻常的结果,例如,这里有一些不同代码点的长度和大小. 下面是我如何生成这些表的片段.
...
UTFCRUDResultStrings.add('INPUT: '+#9#9+ DATA +#9#9+ 'GetFirstCodePointSize = ' +intToStr(GetFirstCodepointSize(DATA))
+#9#9+ 'Length =' + intToStr(length(DATA)));
...
Run Code Online (Sandbox Code Playgroud)
第一组:这对我来说很有意义,每个代码点大小加倍,但每个都是一个字符,Delphi给我的长度只有1,完美.
INPUT: ? GetFirstCodePointSize = 2 Length =1
INPUT: ? GetFirstCodePointSize = 2 Length =1
INPUT: ? GetFirstCodePointSize = 2 Length =1
Run Code Online (Sandbox Code Playgroud)
第二集:它最初看起来像长度和代码点是相反的?我猜这是因为字符+代理被单独处理,因此第一个代码点大小是'H',即1,但长度返回'H'加'^'的长度.
INPUT: H? GetFirstCodePointSize = 1 Length =2
INPUT: a?? GetFirstCodePointSize = 1 Length =3
INPUT: V? GetFirstCodePointSize = 1 Length =2
INPUT: e GetFirstCodePointSize = 1 Length =1
Run Code Online (Sandbox Code Playgroud)
一些额外的测试......
INPUT: ¼ GetFirstCodePointSize = 2 Length =1
INPUT: ? GetFirstCodePointSize = 3 Length =1
INPUT: GetFirstCodePointSize = 4 Length =2
INPUT: ß GetFirstCodePointSize = 2 Length =1
INPUT: GetFirstCodePointSize = 4 Length =2
Run Code Online (Sandbox Code Playgroud)
Delphi中有一种可靠的方法来确定Unicode String中元素的开始和结束位置吗?
我知道使用单词element的术语可能是关闭的,但我不认为代码点和字符也是正确的,特别是考虑到一个元素的代码点大小为3,但长度只有一个.
Rem*_*eau 17
我试图更好地理解Delphi中的代理对和Unicode实现.
让我们一些术语.
由Unicode定义的每个"字符"(称为字形)都被分配了唯一的代码点.
在一个Unicode转换格式(UTF)编码- UTF-7,UTF-8,UTF-16,和UTF-32 -每个码点被编码为序列CODEUNITS.每个代码单元的大小由编码决定 - UTF-7为7位,UTF-8为8位,UTF-16为16位,UTF-32为32位(因此为其名称).
在Delphi 2009及更高版本中,String
是别名UnicodeString
,并且Char
是别名WideChar
. WideChar
是16位.A UnicodeString
保存一个UTF-16编码的字符串(在早期版本的Delphi中,等效的字符串类型是WideString
),每个WideChar
都是一个UTF-16代码单元.
在UTF-16中,可以使用1或2个代码单元对代码点进行编码.1个codeunit可以编码基本多语言平面(BMP)范围内的代码点值 - $ 0000到$ FFFF(含).较高的代码点需要2个代码单元,也称为代理对.
如果我在Delphi中对Unicode字符串S:='ĤàVe'调用length(),我会回来,8.
这是因为各个字符[Ĥ],[à],[V]和[e]的长度分别为2,3,2和1.
这是因为Ĥ有代理人,有两个额外的代理人,V有代理人,e没有代理人.
是的,WideChar
您的UTF-16中有8个元素(codeunits)UnicodeString
.你所谓的"代理人"实际上被称为"组合标记".每个组合标记都是它自己唯一的代码点,因此也就是它自己的代码序列.
如果我想返回字符串中的第二个元素,包括所有代理,[à],我该怎么做?
你必须从头开始UnicodeString
并分析每WideChar
一个,直到找到一个不是前一个附加的组合标记WideChar
.在Windows上,最简单的方法是使用该CharNextW()
功能,例如:
var
S: String;
P: PChar;
begin
S := 'H?a??V?e';
P := CharNext(PChar(S)); // returns a pointer to a??
end;
Run Code Online (Sandbox Code Playgroud)
Delphi RTL没有等效功能.您可以手动编写一个,也可以使用第三方库.RTL确实有一个StrNextChar()
功能,但它只处理UTF-16代理,而不是组合标记(CharNext()
处理两者).因此,您可以使用StrNextChar()
扫描每个代码点UnicodeString
,但是您必须在每个代码点上知道它是否是组合标记,例如:
uses
Character;
function MyCharNext(P: PChar): PChar;
begin
if (P <> nil) and (P^ <> #0) then
begin
Result := StrNextChar(P);
while GetUnicodeCategory(Result^) = ucCombiningMark do
Result := StrNextChar(Result);
end else begin
Result := nil;
end;
end;
var
S: String;
P: PChar;
begin
S := 'H?a??V?e';
P := MyCharNext(PChar(S)); // should return a pointer to a??
end;
Run Code Online (Sandbox Code Playgroud)
我知道我需要对各个字节进行某种测试.
不是字节,而是解码时它们代表的代码点.
我使用例程运行了一些测试
function GetFirstCodepointSize(const S:UTF8String):Integer
仔细查看该功能签名.查看参数类型?它是UTF-8字符串,而不是UTF-16字符串.甚至在你得到该功能的答案中也说明了这一点:
以下是如何解析UTF8字符串的示例
UTF-8和UTF-16是非常不同的编码,因此具有不同的语义.您不能使用UTF-8语义来处理UTF-16字符串,反之亦然.
Delphi中有一种可靠的方法来确定Unicode String中元素的开始和结束位置吗?
不是直接的.您必须从头开始解析字符串,根据需要跳过元素,直到找到所需的元素.请记住,每个代码点可以编码为1或2个代码单元,并且每个逻辑字形可以使用多个代码点(以及多个代码单元序列)进行编码.
我知道使用单词element的术语可能是关闭的,但我不认为代码点和字符也是正确的,特别是考虑到一个元素的代码点大小为3,但长度只有一个.
1个字形由1个以上的代码点组成,每个代码点编码为1个以上的代码单元.
有人可以实现以下功能吗?
function GetElementAtIndex(S:String; StrIdx:Integer):String;
尝试这样的事情:
uses
SysUtils, Character;
function MyCharNext(P: PChar): PChar;
begin
Result := P;
if Result <> nil then
begin
Result := StrNextChar(Result);
while GetUnicodeCategory(Result^) = ucCombiningMark do
Result := StrNextChar(Result);
end;
end;
function GetElementAtIndex(S: String; StrIdx : Integer): String;
var
pStart, pEnd: PChar;
begin
Result := '';
if (S = '') or (StrIdx < 0) then Exit;
pStart := PChar(S);
while StrIdx > 1 do
begin
pStart := MyCharNext(pStart);
if pStart^ = #0 then Exit;
Dec(StrIdx);
end;
pEnd := MyCharNext(pStart);
{$POINTERMATH ON}
SetString(Result, pStart, pEnd-pStart);
end;
Run Code Online (Sandbox Code Playgroud)