2012-04-30 2 views
1

Я использую Scintilla и устанавливаю его в utf8 (и это единственный способ сделать его совместимым с символами Unicode, если я его правильно понимаю). С этой настройкой, когда речь идет о позициях в тексте Scintilla означает байт позиций.Преобразование char pos UnicodeString в byte pos в строку utf8

Проблема в том, что я использую UnicodeString в остальной части моей программы, и когда мне нужно выбрать конкретный звонок в редакторе Scintilla, мне нужно преобразовать из char pos UnicodeString в байт pos в строку utf8, которая соответствующий UnicodeString. Как я могу сделать это легко? Благодарю.

PS, когда я нашел ByteToCharIndex Я думал, что это то, что мне нужно, в соответствии с его документом и результатом моего тестирования, оно работает только если система использует многобайтную систему символов (MBCS).

+0

Вы уверены, что ByteToCharIndex Безразлично Работа? Я не удивлюсь, если текст документации предшествует Delphi 2009, когда AnsiString изменил свою собственную кодовую страницу. Теперь, когда AnsiString содержит кодовую страницу, функция должна быть способна определить, закодирована ли строка как MBCS, SBCS или UTF-8, вместо того, чтобы полагаться на системные настройки. –

+0

@RobKennedy - Это не работает; Более того, функция Windows «CharNextExA» также не работает с UTF8. – kludg

+0

Да, это не работает, поскольку Серг подтвердил, я тоже попробовал. –

ответ

3

Вы должны самостоятельно разобрать строки UTF8, используя UTF8 description. Я написал быстрый аналог 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; 
+0

Спасибо, Серг! Хотя ваша функция UTF8PosToCharIndex определенно полезна, на самом деле, то, что я задаю в этом вопросе SO, является противоположной функцией CharIndexToUTF8Pos. И ваш func здесь - отличный пример для меня (я попытаюсь изучить спецификацию utf-8 и посмотреть, смогу ли я ее реализовать, или, может быть, вы можете дать мне подсказку :) –

+0

@EdwinYip - Я написал короткий блог сообщение, связанное с вашим вопросом - http://sergworks.wordpress.com/2012/05/01/parsing-utf8-strings/ – kludg

+0

Спасибо за вашу помощь, и я решил свою проблему с помощью функции, полученной из вашего кода, и я разместил ее как отдельный ответ ниже. –

0

И UTF-8 и UTF-16 (то, что используются UnicodeString), являются кодировками переменной длины. Данный код кодировки Unicode может быть закодирован в UTF-8, используя между 1-4 однобайтовыми кодовыми единицами и в UTF-16 с использованием 1 или 2 2-байтовых кодовых элементов, в зависимости от числового значения кодовой точки. Единственный способ перевести позицию в строке UTF-16 в позицию в эквивалентной строке UTF-8 состоит в том, чтобы декодировать UDF-16 кодовые модули, предшествующие позиции, до их исходных кодовых значений Unicode, а затем перекодировать их в UTF- 8 кодовых элементов.

Похоже, вы лучше повторно сочинительство код, который взаимодействует с Scintilla использовать UTF8String вместо UnicodeString, то вам не нужно будет переводить между UTF-8 и UTF-16 в этом слое больше. При взаимодействии с остальной частью кода вы можете конвертировать между UTF8String и UnicodeString по мере необходимости.

+0

3rd party lib. Я использую дескрипторы UnicodeString ... И удобство использования UnicodeString (aka string в D2009 и выше) заключается в том, что нам не нужно заботиться о его внутренней кодировке, мы можем просто рассматривать строку как «массив символов», компилятор заботится о том, принимает ли один символ 2 байта или более байтов. –

1

Я написал функцию, основанную на коде Serg с большой уважение, я разместил его здесь как отдельный ответ с надеждой на то, что он тоже полезен для других. Вместо этого принят ответ Серга.

{Вернуть индекс (на основе 1) первого байта символа (unicode point) , указанный aCharIdx (на основе 1) в aUtf8Str.

Кодекс дополняется Edwin Ип на основе кода, написанного 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; 
Смежные вопросы