Неправильное преобразование Unicode, как сохранить символы с диакритикой в исходном коде Delphi 2010 и обработать наборы символов?

Мы обновляем наш проект от Delphi 2006 к Delphi 2010. Старый код был:

InputText: string;
InputText := SomeTEditComponent.Text;
...
for i := 1 to length(InputText) do
if InputText[i] in ['0'..'9', 'a'..'z', 'Ř' { and more special characters } ] then ...

Проблема с буквами диакритического знака - выдерживают сравнение, перестанет работать.

Я попробовал исходный код переключателя от ANSI до UTF8 и LE UCS-2, но без удачи. Только бросок как AnsiChar работает:

if CharInSet(AnsiChar(InputText[i]), ['0'..'9', 'a'..'z', 'Ř']) then

Забавный то, как работы Delphi с этим, в котором буквы - пробуют это, Оценивают во время отладки:

Ord('Ř') = Ord('Ø')

(да, Delphi говорит Правда, на чехе Windows 7),


Вопрос: Как я могу сохранить и сравнить простые строки, не вынуждая их как AnsiStrings? Поскольку, если это не работает, почему мы должны использовать Unicode?

Благодарит все ответ

Прямо сейчас мы используем в некоторых частях простой CharInSet (AnsiChar (...

7
задан DiGi 4 August 2010 в 07:52
поделиться

5 ответов

Как упоминал Уве Раабе, проблема с Unicode char в том, что они довольно большие. Если бы Delphi позволил вам создать "набор Char", то его размер был бы 8 Кб! А "набор AnsiChar" имеет размер всего 32 байта, что вполне управляемо.

Я хотел бы предложить несколько альтернатив. Первая - это своего рода замена функции CharInSet, которая использует массив CHAR для выполнения тестов. Единственное ее достоинство в том, что она может быть вызвана немедленно практически из любого места, но на этом ее преимущества заканчиваются. Я бы избегал ее, если бы мог:

function UnicodeCharInSet(UniChr:Char; CharArray:array of Char):Boolean;
var i:Integer;
begin
  for i:=0 to High(CharArray) do
    if CharArray[i] = UniChr then
    begin
      Result := True;
      Exit;
    end;
  Result := False;
end;

Проблема этой функции в том, что она не обрабатывает синтаксис x в ['a'...z'] и она медленная! Альтернативные варианты быстрее, но не настолько близки к универсальной замене, как хотелось бы. Первым набором альтернатив, которые необходимо исследовать, являются строковые функции от Microsoft. Среди них есть IsCharAlpha и IsCharAlphanumeric, они могут решить множество проблем. Проблема в том, что все символы "альфа" одинаковы: вы можете столкнуться с тем, что в неанглийском и нечешском языках символы "альфа" не будут корректными. В качестве альтернативы вы можете использовать класс TCharacter от Embarcadero - его реализация находится в блоке Character.pas, и она выглядит эффективной, я понятия не имею, насколько эффективна реализация Microsoft.

Другая альтернатива - написать свои собственные функции, используя оператор "case", чтобы заставить все работать. Вот пример:

function UnicodeCharIs(UniChr:Char):Boolean;
var i:Integer;
begin
  case UniChr of
    'ă': Result := True;
    'ş': Result := False;
    'Ă': Result := True;
    'Ş': Result := False;
    else Result := False;
  end;
end;

Я проверил ассемблер, сгенерированный для этой функции. Хотя для этого Delphi должен реализовать серию условий "if", он делает это очень эффективно, гораздо лучше, чем реализация серии операторов IF из кода. Но его можно значительно улучшить.

Для тестов, которые используются много, вы можете поискать реализацию на основе битовых масок.

2
ответ дан 6 December 2019 в 19:31
поделиться

Объявление CharInSet -

function CharInSet(C: AnsiChar; const CharSet: TSysCharSet): Boolean; overload; inline;
function CharInSet(C: WideChar; const CharSet: TSysCharSet): Boolean; overload; inline;

, а TSysCharSet -

TSysCharSet = set of AnsiChar;

. Таким образом, CharInSet может сравниваться только с набором AnsiChar. Вот почему ваш акцентный символ конвертируется в AnsiChar.

Нет эквивалента набору WideChar , поскольку наборы ограничены 256 элементами. Вы должны реализовать другие средства для проверки персонажа.

Можно попробовать что-нибудь вроде

const
  specials: string = 'Ř';

if CharInSet(InputText[i], ['0'..'9', 'a'..'z']) or (Pos(InputText[I], specials) > 0) then 

. При необходимости вы можете добавить больше символов в специальные .

5
ответ дан 6 December 2019 в 19:31
поделиться

Вам следует либо использовать IFs вместо IN, либо найти реализацию WideCharSet. Это может помочь, если у вас много наборов: http://code.google.com/p/delphilhlplib/source/browse/trunk/Library/src/Extensions/DeHL.WideCharSet.pas.

1
ответ дан 6 December 2019 в 19:31
поделиться

Не полагайтесь на кодировку ваших файлов исходного кода Delphi.

Она может быть искажена при использовании любого инструмента, не относящегося к Unicode, для работы с вашими текстовыми файлами (или даже с ошибками в инструментах, поддерживающих Unicode).

Лучший способ - указать символы в виде 4-значной кодовой точки Unicode.

const
  MyEuroSign = #$20AC;

См. также мою запись в блоге об этом.

4
ответ дан 6 December 2019 в 19:31
поделиться

Вы наткнулись на случай, когда идиому из Pre-Unicode Pascal нельзя переводить напрямую в наиболее визуально похожую идиому в паскале эры Unicode.

Во-первых, давайте разберемся со строковыми литералами Юникода. Если вы всегда можете быть уверены, что у вас никогда не будет никакого тела, которое когда-либо будет использовать ваш исходный код с каким-либо инструментом, который может испортить ваши кодировки тогда вы можете использовать литералы Unicode. Лично я не хотел бы видеть кодовые точки Unicode в строковых литералах в любом из моих кодов по разным причинам, самая серьезная причина в том, что мой код может потребоваться пересмотреть на предмет интернационализации в какой-то момент и иметь литералы, принадлежащие вашему местному языку. пропитание вашего кода еще большей проблемой, когда вы используете язык, отличный от тех, которые используют простые символы кодовой страницы Ascii / Ansi.Ваш исходный код будет более читабельным, если вы будете иметь в виду предположение, что ваши акцентированные символы и даже неакцентированные символьные литералы будут лучше объявлены, как говорит Йерун, чтобы объявить их в разделе const, вдали от вашего фактического места в код, который вы их используете.

Рассмотрим случай, когда вы используете один и тот же строковый литерал тридцать три раза во всем коде. Почему его следует повторять вместо константы? И даже когда он используется только один раз, разве код не будет более читабельным, если вы объявите разумное имя константы?

Итак, сначала вы должны объявить константы, как он показывает.

Во-вторых, функция CharInSet устарела для всех видов использования, кроме тех, для которых она была предназначена, где вы должны продолжать использовать типы «Набор AnsiChar». Это больше не рекомендуется в Delphi 2009/2010, и использование массивов буквальных символов Юникода в разделе констант будет более читабельным и актуальным.

Я предлагаю вам использовать функцию JCL StrContainsChars и избегать наборов символов, поскольку вы вообще не можете объявить встроенный набор символов Unicode, язык не позволяет этого. Вместо этого используйте это и обязательно прокомментируйте его:

implementation
uses
   JclStrings;

    const
       myChar1 = #$2001;
       myChar2 = #$2002;
       myChar3 = #$2003;
       myMatchList1 : Array[0..2] of Char = (myChar1,myChar2,myChar3);




function Match(s:String):Boolean;
begin
        result := StrContainsChars( s, myMatchList1,false);

end;

Строковые и символьные литералы - это плохо, если вы добавляете в ваш код добавку, особенно символьные или числовые литералы, называются «магическими значениями», и их следует избегать.

P.S. Утверждение отладки показывает, что Ord ('?') Незаметно понижает значение символа Unicode до символа размера байта AnsiChar в отладчике. Такое поведение является неожиданным и, вероятно, должно быть зарегистрировано в QC.

1
ответ дан 6 December 2019 в 19:31
поделиться
Другие вопросы по тегам:

Похожие вопросы: