繁体   English   中英

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

[英]Convert char pos of UnicodeString to byte pos in a utf8 string

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

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

PS,当我发现ByteToCharIndex我认为这是我需要的,但是,根据它的文档和我的测试结果,它只能工作如果系统使用多字节字符系统(MBCS)。

您应该使用UTF8描述自己解析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;

我非常尊重地写了一个基于Serg代码的函数,我把它作为一个单独的答案发布在这里,希望它对其他人也有帮助。 相反,接受Serg的答案。

{返回aUffStr中由aCharIdx(从1开始)指定的字符(unicode点)的第一个字节的索引(从1开始)。

代码由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;

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之间进行转换。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM