我使用Scintilla并将其编码设置为utf8(如果我理解正确的话,这是使其与Unicode字符兼容的唯一方法)。通过此设置,当在文本中讨论位置时,Scintilla意味着字节位置。
问题是,我在我的程序的其余部分使用UnicodeString,当我需要在Scintilla编辑器中选择一个特定的范围时,我需要将UnicodeString的char pos转换为相应的utf8字符串中的字节pos到UnicodeString。我怎么能这么容易呢?感谢。
PS,当我发现ByteToCharIndex时,我认为这就是我所需要的,但是,根据其文档和测试结果,它只有在系统使用多字节字符系统(MBCS)时才有效。
答案 0 :(得分:3)
您应该使用UTF8 description自行解析UTF8字符串。我写了一个快速的UTF8模拟ByteToCharIndex
并用西里尔字符串测试:
function UTF8PosToCharIndex(const S: UTF8String; Index: Integer): Integer;
var
I: Integer;
P: PAnsiChar;
begin
Result:= 0;
if (Index <= 0) or (Index > Length(S)) then Exit;
I:= 1;
P:= PAnsiChar(S);
while I <= Index do begin
if Ord(P^) and $C0 <> $80 then Inc(Result);
Inc(I);
Inc(P);
end;
end;
const TestStr: UTF8String = 'abФЫВА';
procedure TForm1.Button2Click(Sender: TObject);
begin
ShowMessage(IntToStr(UTF8PosToCharIndex(TestStr, 1))); // a = 1
ShowMessage(IntToStr(UTF8PosToCharIndex(TestStr, 2))); // b = 2
ShowMessage(IntToStr(UTF8PosToCharIndex(TestStr, 3))); // Ф = 3
ShowMessage(IntToStr(UTF8PosToCharIndex(TestStr, 5))); // Ы = 4
ShowMessage(IntToStr(UTF8PosToCharIndex(TestStr, 7))); // В = 5
end;
反向功能也没问题:
function CharIndexToUTF8Pos(const S: UTF8String; Index: Integer): Integer;
var
P: PAnsiChar;
begin
Result:= 0;
P:= PAnsiChar(S);
while (Result < Length(S)) and (Index > 0) do begin
Inc(Result);
if Ord(P^) and $C0 <> $80 then Dec(Index);
Inc(P);
end;
if Index <> 0 then Result:= 0; // char index not found
end;
答案 1 :(得分:1)
我非常尊重地写了一个基于Serg代码的函数,我把它作为一个单独的答案发布在这里,希望它对其他人也有帮助。而Serg的答案却被接受了。
{返回字符第一个字节的索引(从1开始)(unicode point) 由aCharIdx(从1开始)在aUtf8Str。
中指定Edwin Yip根据SO成员Serg(https://stackoverflow.com/users/246408/serg)编写的代码对代码进行了修订
参考1:https://stackoverflow.com/a/10388131/133516
参考2:http://sergworks.wordpress.com/2012/05/01/parsing-utf8-strings/ }
function CharPosToUTF8BytePos(const aUtf8Str: UTF8String; const aCharIdx:
Integer): Integer;
var
p: PAnsiChar;
charCount: Integer;
begin
p:= PAnsiChar(aUtf8Str);
Result:= 0;
charCount:= 0;
while (Result < Length(aUtf8Str)) do
begin
if IsUTF8LeadChar(p^) then
Inc(charCount);
if charCount = aCharIdx then
Exit(Result + 1);
Inc(p);
Inc(Result);
end;
end;
答案 2 :(得分:0)
UTF-8和UTF-16(UnicodeString
使用的)都是可变长度编码。给定的Unicode代码点可以使用1-4个单字节代码单元以UTF-8编码,使用1或2个2字节代码单元以UTF-16编码,具体取决于代码点的数值。将UTF-16字符串中的位置转换为等效UTF-8字符串中的位置的唯一方法是将位置之前的UTF-16代码单元解码回其原始Unicode代码点值,然后将它们重新编码为UTF- 8个codeunits。
听起来您最好重新编写与Scintilla交互的代码以使用UTF8String
而不是UnicodeString
,那么您不必在UTF-8和UTF-16之间进行转换在那一层。在与代码的其余部分进行交互时,您可以根据需要在UTF8String
和UnicodeString
之间进行转换。