Как манипулировать подстроки, а не подмассивы UnicodeString?
Я тестирую переход с Delphi 5 на XE. Будучи незнакомым с UnicodeString, прежде чем задать свой вопрос, я хотел бы представить его фон.
Строково-ориентированные функции Delphi XE: Copy, Delete и Insert имеют параметр Index, указывающий, где должна начинаться операция. Индекс может иметь любое целочисленное значение, начиная с 1 и заканчивая длиной строки, к которой применяется функция. Поскольку строка может содержать многоэлементные символы, работа функции может начинаться с элемента (суррогата), принадлежащего многоэлементной серии, кодирующей один юникод с именем code-point. Тогда, имея разумную строку и используя одну из функций, мы можем получить непредсказуемый результат.
Это явление можно проиллюстрировать в следующих случаях, используя функцию Copy в отношении строк, представляющих один и тот же массив именованных кодовых точек (т. Е. Значимых знаков).
($61, $13000, $63)
Это соединение 'a'
, EGYPTIAN_HIEROGLYPH_A001
а также 'c'
; это выглядит как
Случай 1. Копия AnsiString (элемент = байт)
Начнем с вышеупомянутой UnicodeString #$61#$13000#$63
и мы конвертируем его в кодировку UTF-8 AnsiString s0
,
Затем мы проверяем функцию
copy (s0, index, 1)
для всех возможных значений индекса; с тех пор их 6 s0
длиной 6 байт.
procedure Copy_Utf8Test;
type TAnsiStringUtf8 = type AnsiString (CP_UTF8);
var ss : string;
s0,s1 : TAnsiStringUtf8;
ii : integer;
begin
ss := #$61#$13000#$63; //mem dump of ss: $61 $00 $0C $D8 $00 $DC $63 $00
s0 := ss; //mem dump of s0: $61 $F0 $93 $80 $80 $63
ii := length(s0); //sets ii=6 (bytes)
s1 := copy(s0,1,1); //'a'
s1 := copy(s0,2,1); //#$F0 F means "start of 4-byte series"; no corresponding named code-point
s1 := copy(s0,3,1); //#$93 "trailing in multi-byte series"; no corresponding named code-point
s1 := copy(s0,4,1); //#$80 "trailing in multi-byte series"; no corresponding named code-point
s1 := copy(s0,5,1); //#$80 "trailing in multi-byte series"; no corresponding named code-point
s1 := copy(s0,6,1); //'c'
end;
Первый и последний результаты имеют смысл в кодовой странице UTF-8, а остальные 4 - нет.
Случай 2. Копия UnicodeString (элемент = слово)
Мы начинаем с того же UnicodeString s0 := #$61#$13000#$63
,
Затем мы проверяем функцию
copy (s0, index, 1)
для всех возможных значений индекса; с тех пор их 4 s0
4 слова в длину.
procedure Copy_Utf16Test;
var s0,s1 : string;
ii : integer;
begin
s0 := #$61#$13000#$63; //mem dump of s0: $61 $00 $0C $D8 $00 $DC $63 $00
ii := length(s0); //sets ii=4 (bytes)
s1 := copy(s0,1,1); //'a'
s1 := copy(s0,2,1); //#$D80C surrogate pair member; no corresponding named code-point
s1 := copy(s0,3,1); //#$DC00 surrogate pair member; no corresponding named code-point
s1 := copy(s0,4,1); //'c'
end;
Первый и последний результаты имеют смысл в кодовой странице CP_UNICODE
(1200), а другие 2 - нет.
Заключение.
Строково-ориентированные функции: " Копировать", " Удалить" и " Вставить" прекрасно работают со строкой, рассматриваемой как простой массив байтов или слов. Но они не помогают, если строка рассматривается как то, чем она является, то есть представление массива именованных кодов.
Оба вышеупомянутых двух случая имеют дело со строками, которые представляют один и тот же массив из 3 названных кодовых точек. Они рассматриваются как представления (кодировки) одного и того же текста, состоящего из 3 значащих знаков (чтобы избежать злоупотребления термином "символы").
Можно хотеть иметь возможность извлечь (скопировать) любой из этих значимых признаков независимо от того, является ли конкретное текстовое представление (кодирование) одно- или многоэлементным. Я потратил довольно много времени на поиски удовлетворительного эквивалента Copy, который я использовал в Delphi 5.
Вопрос. Существуют ли такие эквиваленты, или я должен написать их сам?
2 ответа
То, что вы описали, как Copy()
, Delete()
, а также Insert()
ВСЕГДА работали, даже для AnsiString
, Функции работают с элементами (то есть с единицами кода в терминологии Unicode) и всегда имеют.
AnsiString
это строка из 8 бит AnsiChar
элементы, которые могут быть закодированы в любом 8-битном формате ANSI/MBCS, включая UTF-8.
UnicodeString
(а также WideString
) это строка из 16 бит WideChar
элементы, которые кодируются в UTF-16.
Функции НИКОГДА не учитывали кодирование. Не для MBCS AnsiString
, Не для UTF-16 UnicodeString
, Индексы - это абсолютные индексы элементов с начала строки.
Если вам нужно кодирование Copy
/Delete
/Insert
функции, которые работают на границах логической кодовой точки, где каждая кодовая точка может быть 1+ элементов в строке, затем вы должны написать свои собственные функции или найти сторонние функции, которые делают то, что вам нужно. В RTL нет функций искажения, поддерживающих MBCS/UTF.
Вы должны разобрать строку Unicode самостоятельно. К счастью, кодировка Unicode разработана для упрощения анализа. Вот пример того, как разобрать строку UTF8:
program Project9;
{$APPTYPE CONSOLE}
uses
SysUtils;
function GetFirstCodepointSize(const S: UTF8String): Integer;
var
B: Byte;
begin
B:= Byte(S[1]);
if (B and $80 = 0 ) then
Result:= 1
else if (B and $E0 = $C0) then
Result:= 2
else if (B and $F0 = $E0) then
Result:= 3
else if (B and $F8 = $F0) then
Result:= 4
else
Result:= -1; // invalid code
end;
var
S: string;
begin
S:= #$61#$13000#$63;
Writeln(GetFirstCodepointSize(S));
S:= #$13000#$63;
Writeln(GetFirstCodepointSize(S));
S:= #$63;
Writeln(GetFirstCodepointSize(S));
Readln;
end.