将UnicodeString的char pos转换为utf8字符串中的byte pos

时间:2012-04-30 15:15:58

标签: delphi delphi-2010

我使用Scintilla并将其编码设置为utf8(如果我理解正确的话,这是使其与Unicode字符兼容的唯一方法)。通过此设置,当在文本中讨论位置时,Scintilla意味着字节位置。

问题是,我在我的程序的其余部分使用UnicodeString,当我需要在Scintilla编辑器中选择一个特定的范围时,我需要将UnicodeString的char pos转换为相应的utf8字符串中的字节pos到UnicodeString。我怎么能这么容易呢?感谢。

PS,当我发现ByteToCharIndex时,我认为这就是我所需要的,但是,根据其文档和测试结果,它只有在系统使用多字节字符系统(MBCS)时才有效。

3 个答案:

答案 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之间进行转换在那一层。在与代码的其余部分进行交互时,您可以根据需要在UTF8StringUnicodeString之间进行转换。

相关问题