1

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

问题是,我在程序的其余部分使用 UnicodeString,当我需要在 Scintilla 编辑器中选择特定范围时,我需要将 UnicodeString 的 char pos 转换为对应于 UnicodeString 的 utf8 字符串中的 byte pos . 我怎样才能轻松做到这一点?谢谢。

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

4

3 回答 3

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;
于 2012-04-30T17:46:37.817 回答
1

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

{返回 aUtf8Str 中由 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;
于 2012-05-01T05:16:11.337 回答
0

UTF-8 和 UTF-16(UnicodeString用途)都是可变长度编码。给定的 Unicode 代码点可以使用 1-4 个单字节代码单元在 UTF-8 中编码,在 UTF-16 中使用 1 或 2 个 2 字节代码单元,具体取决于代码点的数值。将 UTF-16 字符串中的位置转换为等效 UTF-8 字符串中的位置的唯一方法是将位置之前的 UTF-16 代码单元解码回其原始 Unicode 代码点值,然后将它们重新编码为 UTF- 8 个代码单元。

听起来您最好重新编写与 Scintilla 交互的代码来使用UTF8String而不是UnicodeString,然后您就不必再在该层在 UTF-8 和 UTF-16 之间进行转换了。在与其余代码交互时,您可以根据需要在UTF8String和之间进行转换UnicodeString

于 2012-04-30T17:31:39.260 回答