Подтвердить что ты не робот

Предполагая, что Unicode и нечувствительность к регистру, если шаблон ".." соответствует "FfIsS"?

Звучит как шутка, но я могу это доказать.

Предположения:

  • Точка соответствует любому одиночному символу.
  • Нечувствительный к регистру шаблон соответствует s тогда и только тогда, когда он соответствует s.toUpperCase().

Все следующее довольно логично и выполняется в Java:

  • "ffi".matches(".") LATIN SMALL LIGATURE FFI (U + FB03) - символ, поэтому он должен соответствовать
  • "ß".matches(".") LATIN SMALL LETTER SHARP S (U + 00DF) - символ, поэтому он должен соответствовать
  • "ffi".toUpperCase().equals("FFI") по стандарту Unicode (там нет лиги капитала FFI)
  • "ß".toUpperCase().equals("SS") по стандарту Unicode (там есть острый S, но он не используется)
  • "FfI".toUpperCase().equals("FFI") очевидно
  • "sS".toUpperCase.equals("SS") очевидно

Итак, если первая точка в регулярном выражении обозначает , а вторая для ß, регулярное выражение должно соответствовать "FFISS" и из-за нечувствительности к регистру также "FfIsS".

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

Вопросы:

  • Что не так с моим "доказательством"?
  • Что означает "нечувствительность к регистру", если мое второе предположение не выполняется?
4b9b3361

Ответ 1

В случае складывания флагов

Ответ: нет, точка будет не соответствовать ss нечувствительно к регистру, хотя причины немного эзотеричны.

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

В Юникоде существует две формы отображения случаев. Существует простое отображение случаев, в котором одна кодовая точка только когда-либо отображает только одну другую кодовую точку. Поэтому, если length(s) == 1, то вам гарантируется, что length(fc(s)) == 1 также, где fc является картой памяти Unicode. Но это также относится к отображениям uc, tc и lc.

Проблема заключается в том, что вы не получаете столь же хороших результатов, анализируя определенные виды текста в реальном мире, тогда вы делаете такие виды точной гарантии длины 1:1.

На самом деле их довольно много. Цифры указывают, сколько отдельных кодовых точек BMP отображается на указанные длины в четырех картах:

length lc == 2          1
length lc == 3          0

length fc == 2         88
length fc == 3         16

length uc == 2         86
length uc == 3         16

length tc == 2         32
length tc == 3         16

В полном корпусе, а не в простом корпусе, который использует Javas regex, вы можете получить такие вещи, как tschüß и TSCHÜSS, даже если они имеют неравную длину. Perl и Ruby используют полное отображение случаев при выполнении нечувствительных к регистру сравнений. Это приводит к странным парадоксам в отрицательных классах персонажей, если вы не слишком осторожны.

Но это означает, что нечувствительность к тегу: нечувствительность к регистру не выполняет транзитивную операцию. Другими словами, если . соответствует ß и при нечувствительном к регистру совпадении, ß соответствует ss, это не означает, что с помощью транзитивности . не учитывается регистр ss. Это просто так не работает, хотя более умные люди, чем я, глубоко задумались над этим.

Однако обе эти кодовые точки:

  • U + 00DF ß LATIN SMALL LETTER SHARP S
  • U + 1E9E ẞ LATIN CAPITAL LETTER SHARP S

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

Unicode делает некоторые гарантии об этом. Во-первых, если length(s) == n, то length(fn(s)) <= 3*n где fn - любое из четырех карт: lc, fc, uc и tc.

При нормализации

Если вы считаете, что это плохо, на самом деле становится намного хуже, когда вы рассматриваете формы нормализации. Здесь гарантия 5 × не 3 ×. Итак, length(NFx(s)) <= 5 * length(s), который, как вы видите, становится дорогим.

Вот эквивалентная таблица, показывающая, сколько кодовых точек расширяется до более чем одной под каждой из четырех форм нормализации:

length NFC  == 2        70
length NFC  == 3         2
length NFC  == 4         0
length NFC  == 5         0

length NFKC == 2       686
length NFKC == 3       404
length NFKC == 4        53
length NFKC == 5        15

length NFD  == 2       762
length NFD  == 3       220
length NFD  == 4        36
length NFD  == 5         0

length NFKD == 2      1345
length NFKD == 3       642
length NFKD == 4       109
length NFKD == 5        16

Разве это замечательно? Некоторое время Unicode хотел попытаться построить каноническую эквивалентность в соответствие шаблонов. Они знали, что это дорого по только что изложенным причинам, но им потребовалось некоторое время, чтобы понять, что это было принципиально невозможно из-за необходимого канонического переупорядочения сочетания символов внутри одной графемой.

По этой причине и многие другие, текущая рекомендация, если вы хотите сравнить вещи "без учета регистра" или "нормализовать-нечувствительно", - это выполнить ее через преобразование с обеих сторон, а затем сравнить результаты.

Например, с учетом подходящего == оператора эквивалентности кода-точки-кодовой точки

fc(a) == fc(b)

и аналогичным образом для оператора сопоставления шаблонов =~ (который работает традиционным способом, но не как метод Javas broken match, который неправильно привязывает вещи):

fc(a) =~ fc(b)

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

/aaa(?i:xxx)bbb/

и только часть xxx выполняется без учета регистра.

Полная оболочка жесткая, но она может (в большинстве случаев) быть выполнена, как доказали Perl и Ruby. Но это также довольно неинтуитивно (читайте: удивительно) в местах, которые вы должны понимать. Вы должны делать специальные вещи с помощью классов символов в квадратных скобках, особенно с их отрицаниями, или это приводит к бессмыслице.

Согласование локалей

Наконец, чтобы сделать вещи действительно сложными, в реальном мире вам нужно сделать больше, чем одно или оба из картографирования случаев и нормализации. В некоторых национальных локациях все сложнее. Например, в немецкой телефонной книге, а гласный с умлаутом считается точно таким же, как тот же базовый гласный, за которым следует буква e. Таким образом, ожидается, что что-то вроде müß будет соответствовать MUESS без учета регистра.

Чтобы сделать все это правильно, вам действительно нужно привязать не только к полному картографированию и нормализации таблицы, самому DUCET, таблице элементов сортировки Unicode по умолчанию, и даже данные CLDR (см. библиографию):

#!/usr/bin/perl
use utf8;
use open qw(:utf8 :std);
use Unicode::Collate::Locale;

my $Collator = Unicode::Collate::Locale->new(
    locale        => "de__phonebook",
    level         => 1,
    normalization => undef,
);

my $full = "Ich müß Perl studieren.";
my $sub  = "MUESS";
if (my ($pos,$len) = $Collator->index($full, $sub)) {
    my $match = substr($full, $pos, $len);
    print "Found match of literal ‹$sub› at position $pos in ‹$full› as ‹$match›\n";
}

Если вы запустите это, вы обнаружите, что он действительно работает:

Найдено совпадение буквального в позиции 4 в как


Выбранная библиография

Большинство этих примеров были взяты из версии 4 th программирования Perl по любезному разрешению ее автора.:) Я пишу совсем немного о таких вопросах Юникода, но не общих для Perl, но общих для Unicode в целом.

Программа unichars (1), которая позволяет мне собирать такие статистические данные:

$ unichars 'length fc == 2' | wc -l
      88

$ unichars 'length NFKD == 4' | wc -l
     109

$ unichars '/ss/i'
U+00DF ‭ ß  LATIN SMALL LETTER SHARP S
U+1E9E ‭ ẞ  LATIN CAPITAL LETTER SHARP S

Является частью модуля Unicode:: Tussle CPAN, который Брайан Фой был достаточно любезен, чтобы поддерживать меня.


Для дальнейшего чтения

См. также:

Ответ 2

Как отметил в своем комментарии maaartinus, Java предоставляет (по крайней мере теоретически) поддержку Unicode для учета ошибок без учета регистра. Формулировка в документации Java API заключается в том, что сопоставление выполняется "в соответствии с стандартом Unicode". Однако проблема заключается в том, что стандарт Unicode определяет различные уровни поддержки для преобразования case и нечувствительности к регистру, а документация API не указывает, какой уровень поддерживается языком Java.

Хотя документация не документирована, по крайней мере, в Oracle Java VM, реализация reg-exp ограничена так называемым простым нечувствительным к регистру. Ограничивающие факторы, имеющие отношение к вашим данным примера, заключаются в том, что алгоритм сопоставления работает только так, как ожидалось, если свертывание (преобразование) случая приводит к тому же количеству символов и что наборы (например, "." ) Ограничены, чтобы соответствовать точно одному символу на входе строка. Первое ограничение даже приводит к тому, что "ß" не соответствует "SS", как вы, возможно, и ожидали.

Чтобы получить поддержку полного нечувствительного к регистру соответствия между строковыми литералами, вы можете использовать реализацию reg-exp в библиотеке ICU4J, чтобы по крайней мере, "ß" и "SS". AFAIK, однако нет реализаций reg-exp для Java с полной поддержкой групп, наборов и диких карт.