Конкретное регулярное выражение Javascript для акцентированных символов (диакритические знаки)

166

Я посмотрел на переполнение стека ( замена символов ... например , как JavaScript не соответствует стандарту Unicode относительно RegExp и т. Д.) И не нашел конкретного ответа на вопрос:

How can JavaScript match for accented characters (those with diacritical marks)?

Я заставляю поле в пользовательском интерфейсе соответствовать формату: last_name, first_name (последнее [пространство запятой] первым) , и я хочу обеспечить поддержку диакритических знаков, но, очевидно, в JavaScript это немного сложнее, чем в других языках / платформах.

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

/^[a-zA-Z]+,\s[a-zA-Z]+$/

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

Явный список всех акцентированных символов, которые я хотел бы принять как допустимые (хромые и чрезмерно сложные):


var accentedCharacters = "àèìòùÀÈÌÒÙáéíóúýÁÉÍÓÚÝâêîôûÂÊÎÔÛãñõÃÑÕäëïöüÿÄËÏÖÜŸçÇßØøÅåÆæœ";
// Build the full regex
var regex = "^[a-zA-Z" + accentedCharacters + "]+,\\s[a-zA-Z" + accentedCharacters + "]+$";
// Create a RegExp from the string version
regexCompiled = new RegExp(regex);
// regexCompiled = /^[a-zA-ZàèìòùÀÈÌÒÙáéíóúýÁÉÍÓÚÝâêîôûÂÊÎÔÛãñõÃÑÕäëïöüÿÄËÏÖÜŸçÇßØøÅåÆæœ]+,\s[a-zA-ZàèìòùÀÈÌÒÙáéíóúýÁÉÍÓÚÝâêîôûÂÊÎÔÛãñõÃÑÕäëïöüÿÄËÏÖÜŸçÇßØøÅåÆæœ]+$/
  • Это правильно сопоставляет фамилию / имя с любым из поддерживаемых акцентированных символов в accentedCharacters.

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

var regex = /^.+,\s.+$/;
  • Этот матч будет просто ни о чем, по крайней мере , в виде: something, something. Все в порядке, я полагаю ...

Последний подход, который я только что нашел, может быть проще ...

/^[a-zA-Z\u00C0-\u017F]+,\s[a-zA-Z\u00C0-\u017F]+$/
  • Он соответствует ряду символов Юникода - проверено и работает, хотя я не пробовал ничего сумасшедшего, только обычные вещи, которые я вижу в нашем языковом отделе для имен преподавателей.

Вот мои проблемы:

  1. Первое решение слишком ограничивающее, а также неаккуратное и запутанное. Это нужно изменить, если я забуду одного или двух персонажей, и это не очень практично.
  2. Второе решение лучше, лаконичнее, но, вероятно, соответствует гораздо большему, чем должно быть на самом деле. Я не смог найти никакой реальной документации о том, что именно. соответствует, только обобщение «любого символа, кроме символа новой строки» (из таблицы в MDN ).
  3. Третье решение кажется наиболее точным, но есть ли ошибки? Я не очень знаком с Unicode, по крайней мере, на практике, но, глядя на кодовую таблицу / продолжение этой таблицы , \u00C0-\u017Fкажется довольно солидным, по крайней мере, для моего ожидаемого ввода.

    • Преподаватели не будут отправлять формы с их именами на родном языке (например, на арабском, китайском, японском и т. Д.), Поэтому мне не нужно беспокоиться о наборах символов, не входящих в латиницу

Таким образом, реальный вопрос (ы) : Какой из этих трех подходов больше всего подходит для этой задачи? Или есть лучшие решения?

Крис Сирефице
источник
1
Кажется, нет особой причины использовать более сложные регулярные выражения. Единственное, что касается самого простого решения, это то, что оно также будет соответствовать «что-то, что-то, что-то». Вы могли бы использовать что-то вроде, regex = /^[^,]+,\s[^,]+$/;чтобы предотвратить это.
usr2564301
4
На первый взгляд, первое не будет соответствовать общему имени «О'Доннелл, Крис», ни составлять фамилии с дефисом, ни несколько фамилий (и т. Д.). См. Falsehoods Программисты верят в имена для почти всех возможных подводных камней.
usr2564301
« Атом соответствует ничего , кроме символов новой строки » на самом деле является довольно точным :-).
Берги
1
Если вы можете использовать дополнительную библиотеку, посмотрите мой ответ здесь
stema
Jongware, я на самом деле только что прочитал эту статью, пока просматривал SO для ответа на мой вопрос - я также полностью забыл про дефисы и апострофы и тому подобное, я был более заинтересован в том, чтобы сначала сделать его международным: P Я рад, что вы принесли его хотя! И, Stema, я на самом деле посмотрел на эту библиотеку и избегаю включения библиотек, потому что это все в скрипте Google Apps - включение внешних библиотек было бы кошмаром, и я бы использовал его (в данном случае) только для одного конкретного поля ... излишнее
убийство

Ответы:

275

Самый простой способ принять все акценты это:

[A-zÀ-ú] // accepts lowercase and uppercase characters
[A-zÀ-ÿ] // as above but including letters with an umlaut (includes [ ] ^ \ × ÷)
[A-Za-zÀ-ÿ] // as above but not including [ ] ^ \
[A-Za-zÀ-ÖØ-öø-ÿ] // as above but not including [ ] ^ \ × ÷

См. Https://unicode-table.com/en/ для символов, перечисленных в числовом порядке.

Maycow Moura
источник
2
Это работает хорошо, +1, но не могли бы вы пояснить, почему это работает?
Пьер Генри
1
@PierreHenry -определяет диапазон, и этот метод использует упорядочение символов в кодировке для определения непрерывного диапазона, что позволяет получить очень краткое решение проблемы
Angad
8
не будет ли это совпадение подчеркивать (и другие несловарные символы между Zи a)?
Jcuenod
21
Это соответствует как минимум символам [,], ^ и \, ни один из которых не должен быть включен.
конец
2
Не работает, несколько символов в этом диапазоне не являются символами ударения (например, U + 00D7 - знак умножения), см. Это: unicode-table.com/en
Жереми Пуе
39

Акцентированного латинского диапазона \u00C0-\u017Fбыло недостаточно для моей базы данных имен, поэтому я расширил регулярное выражение до

[a-zA-Z\u00C0-\u024F]
[a-zA-Z\u00C0-\u024F\u1E00-\u1EFF] // includes even more Latin chars

Я добавил эти блоки кода ( \u00C0-\u024Fвключает три смежных блока одновременно):

Обратите внимание, что \u00C0-\u00FFна самом деле это только часть дополнения Latin-1 . Этот диапазон пропускает непечатаемые управляющие сигналы и все символы, кроме неуклюжего умножения × \u00D7и деления ÷ \u00F7.

[a-zA-Z\u00C0-\u00D6\u00D8-\u00F6\u00F8-\u024F] // exclude ×÷

Если вам нужно больше кодовых точек, вы можете найти больше диапазонов в списке символов Юникода в Википедии . Например, вы также можете добавить Latin Extended-C , D и E , но я их исключил, потому что сейчас их интересуют только историки, а наборы D и E даже неправильно отображаются в моем браузере.

Первоначальное регулярное выражение останавливалось \u017Fна названии "onenol". Согласно Unicode Analyzer FontSpace , первым символом является \u0218LATIN CAPITAL LETTER S с запятой ниже. (Да, обычно это пишется с помощью cedilla-S \u015E, «Шенол». Но я не лечу в Турцию, чтобы сказать ему: «Вы пишете свое имя неправильно!»)

Хаим Лейб Хальберт
источник
1
Посмотрев на юникода таблицы латинского блока , я думаю , вы должны также включать \ u1e00- \ u1eff, так что я делаю[a-zA-Z\u00c0-\u024f\u1e00-\u1eff]
cprcrack
18

Какой из этих трех подходов больше всего подходит для этой задачи?

Зависит от задачи :-) Чтобы соответствовать точно всем латинским символам и их акцентированным версиям, диапазоны Unicode, вероятно, обеспечивают лучшее решение. Они могут быть распространены на все непробельные символы, что можно сделать с помощью \Sкласса символов.

Я заставляю поле в пользовательском интерфейсе соответствовать формату: last_name, first_name(последний [запятая] первый)

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

/[^,]+,\s[^,]+/

Но ваше второе решение с .классом символов так же хорошо, вам может понадобиться только несколько запятых.

Берги
источник
Хм, может ты и прав. Я, вероятно, слишком усложнил это ... Не могли бы вы объяснить приведенное вами регулярное выражение? Я немного поработал с regex, но только с базовыми вещами, и на самом деле я понятия не имею, что на самом деле делает ваш! Ха
Крис Cirefice
Это отрицательный класс символов, означающий «что угодно, кроме запятой».
Берги
Ах, так это больше похоже на any_character_not_a_comma, any_character_not_a_comma? Это то, что я подумал, когда впервые прочитал это, я немного растерялся, когда увидел там три запятых.
Крис Cirefice
Да, точно. Извините за путаницу с пропавшими без вести s...
Берги
1
@ MateoTibaquirá Вы можете упростить [^\s]до\S
Берги
15

В библиотеке XRegExp есть плагин с именем Unicode, который помогает решать подобные задачи.

<script src="xregexp.js"></script>
<script src="addons/unicode/unicode-base.js"></script>
<script>
  var unicodeWord = XRegExp("^\\p{L}+$");

  unicodeWord.test("Русский"); // true
  unicodeWord.test("日本語"); // true
  unicodeWord.test("العربية"); // true
</script>

Это упоминается в комментариях к вопросу, но это легко пропустить. Я заметил это только после того, как отправил этот ответ.

колючка
источник
Хорошо, получается, что на самом деле мне не нужно регулярное выражение для юникода, а скорее для шаблона anything, anything. Это будет полезно для будущих читателей :)
Крис Сирефице
12

Как насчет этого?

/^[a-zA-ZÀ-ÖØ-öø-ÿ]+$/
alchn
источник
2
Не соответствует Šš.
Gajus
5

Как насчет этого?

^([a-zA-Z]|[à-ú]|[À-Ú])+$

Это будет соответствовать каждому слову с ударением символов или нет.

Хавьер Палларес
источник
2
Но ОП хочет разрешить акцентированные символы.
Барбсан
3
/^[\pL\pM\p{Zs}.-]+$/u

Объяснение:

  • \pL - соответствует любому виду букв на любом языке
  • \pM - выделяет символ, предназначенный для объединения с другим персонажем (например, акценты, умлауты, заключенные в рамки и т. д.)
  • \p{Zs} - соответствует символу пробела, который невидим, но занимает место
  • u - Шаблонные и предметные строки рассматриваются как UTF-8

В отличие от других предложенных регулярных выражений (таких как [A-Za-zÀ-ÖØ-öø-ÿ]), это будет работать со всеми символами, специфичными для языка, например Šš, соответствует этому правилу, но не соответствует другим на этой странице.

К сожалению, изначально JavaScript не поддерживает эти классы. Тем не менее, вы можете использовать xregexp, например,

const XRegExp = require('xregexp');

const isInputRealHumanName = (input: string): boolean => {
  return XRegExp('^[\\pL\\pM-]+ [\\pL\\pM-]+$', 'u').test(input);
};
Gajus
источник
1

Вы можете удалить диакритические знаки из алфавитов, используя:

var str = "résumé"`
str.normalize('NFD').replace(/[\u0300-\u036f]/g, '')` // returns resume

Он удалит все диакритические знаки, а затем выполнит ваше регулярное выражение

Ссылка:

https://thread.engineering/2018-08-29-searching-and-sorting-text-with-diacritical-marks-in-javascript/

javadb9
источник