Простой материал
Несмотря на неточное утверждение, что это невозможно с помощью регулярных выражений, это, безусловно, так.
Хотя @cjm справедливо заявляет, что намного проще отрицать положительное совпадение, чем выражать отрицательное в качестве единого шаблона, модель для этого достаточно известна, так что это становится просто вопросом подключения вещи в эту модель. Учитывая, что:
/X/
соответствует чему-то, то способ выражения условия
! /X/
в одном, положительно совпадающем шаблоне - записать его как
/\A (?: (?! X ) . ) * \z /sx
Следовательно, учитывая, что положительный паттерн равен
/ (\pL) .* \1 /sxi
соответствующие отрицательные потребности должны быть
/\A (?: (?! (\pL) .* \1 ) . ) * \z /sxi
путем простой замены X.
Реальные проблемы
Тем не менее, существуют смягчающие проблемы, которые иногда могут требовать дополнительной работы. Например, хотя \pL
описывает любую кодовую точку, имеющую свойство GeneralCategory = Letter , он не учитывает, что делать со словами, такими как красный-фиолетовый , ' Это не , или невеста - последний из которых отличается в других эквивалентных NFD против NFC формах.
Поэтому вы должны сначала выполнить его через полную декомпозицию, чтобы строка, подобная "r\x{E9}sume\x{301}"
, правильно обнаруживала дублирующиеся «буквы é 's», то есть все канонически эквивалентные единицы кластера графем.
Чтобы учесть такие, как они, вы должны, как минимум, сначала провести вашу строку через декомпозицию NFD, а затем впоследствии также использовать кластеры графем через \X
вместо произвольных кодовых точек через .
.
Таким образом, для английского языка вам нужно что-то, что следует за этими линиями для положительного соответствия, с соответствующим отрицательным соответствием для замены, приведенной выше:
NFD($string) =~ m{
(?<ELEMENT>
(?= [\p{Alphabetic}\p{Dash}\p{Quotation_Mark}] ) \X
)
\X *
\k<ELEMENT>
}xi
Но даже при этом все еще остаются нерешенными некоторые нерешенные вопросы, такие как, например, следует ли считать \N{EN DASH}
и \N{HYPHEN}
эквивалентными элементами или различными.
Это потому, что правильно написанные дефисы двух элементов, таких как красный фиолетовый и цветной , образуют одно составное слово красный фиолетовый , где, по крайней мере, одна из пары уже содержит дефис , требует, чтобы в качестве разделителя использовался EN DASH вместо простого HYPHEN.
Обычно EN DASH зарезервирован для соединений подобной природы, таких как компромисс между временем и пространством . Люди, использующие пишущую машинку - английский даже не делают этого, хотя и используют этот сверхнормативно перегруженный устаревший код HYPHEN-MINUS для обоих: красно-фиолетового цвета .
Это просто зависит от того, был ли ваш текст написан на какой-то ручной пишущей машинке 19-го века или представляет собой текст на английском языке, правильно отрисованный по современным правилам набора текста. :)
сознательная нечувствительность к регистру
Вы заметите, что я здесь рассматриваю письмо, которое отличается только в том случае, если оно совпадает. Это потому, что я использую /i
переключатель регулярных выражений, (?i)
модификатор шаблона.
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *1078* * * * * * * * * *1078* * * * * * * * * * * * * * * * * * по крайней мере *1078*. 1082 *) для совпадений без учета регистра, а не какой-либо более высокой силы сопоставления, чем третичный уровень, что может быть предпочтительным.
Полная эквивалентность по первичной силе сопоставления является значительно более сильным утверждением, но оно вполне может потребоваться для полного решения проблемы в общем случае. Тем не менее, , что требует гораздо больше работы, чем та, которая требуется во многих конкретных случаях. Короче говоря, для многих конкретных случаев это избыточно, независимо от того, сколько это может понадобиться для гипотетического общего случая.
Это стало еще сложнее, потому что, хотя вы можете, например, сделать это:
my $collator = new Unicode::Collate::Locale::
level => 1,
locale => "de__phonebook",
normalization => undef,
;
if ($collator->cmp("müß", "MUESS") == 0) { ... }
и ожидайте получить правильный ответ - иВы делаете, ура! - такого рода надежное сравнение строк нелегко распространить на совпадения регулярных выражений.
Тем не менее. :)
Резюме
Выбор того, будет ли инженером - или инженером - решение, будет зависеть от индивидуальных обстоятельств, которые никто не может решить за вас.
Мне нравится решение CJM, которое сводит на нет положительное совпадение, самому, хотя оно несколько капризнее в том, что оно считает дубликатом письма. Примечание:
while ("de__phonebook" =~ /(?=((\w).*?\2))/g) {
print "The letter <$2> is duplicated in the substring <$1>.\n";
}
производит:
The letter <e> is duplicated in the substring <e__phone>.
The letter <_> is duplicated in the substring <__>.
The letter <o> is duplicated in the substring <onebo>.
The letter <o> is duplicated in the substring <oo>.
Это показывает, почему, когда вам нужно сопоставить букву, вы должны alwasy использовать \pL
ᴀᴋᴀ \p{Letter}
вместо \w
, что на самом деле соответствует [\p{alpha}\p{GC=Mark}\p{NT=De}\p{GC=Pc}]
.
Конечно, когда вам нужно соответствовать буквенному алфавиту, вам нужно использовать \p{alpha}
ᴀᴋᴀ \p{Alphabetic}
, что совсем не то же самое, что простое письмо - вопреки распространенному заблуждению. :)