Konkretny regeks JavaScript dla znaków akcentowanych (znaki diakrytyczne)

166

Spojrzałem na Stack Overflow ( zastępowanie znaków ... eh , jak JavaScript nie jest zgodny ze standardem Unicode dotyczącym RegExp itp.) I tak naprawdę nie znalazłem konkretnej odpowiedzi na pytanie:

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

Zmuszam pole w interfejsie użytkownika, aby pasowało do formatu: last_name, first_name (najpierw ostatnie [przecinek]) i chcę zapewnić obsługę znaków diakrytycznych, ale najwyraźniej w JavaScript jest to nieco trudniejsze niż w innych językach / platformach.

To była moja oryginalna wersja, dopóki nie chciałem dodać znaków diakrytycznych:

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

Obecnie rozważam jedną z trzech metod dodawania wsparcia, z których wszystkie przetestowałem i działam (przynajmniej do pewnego stopnia nie bardzo wiem, jaki jest „zakres” drugiego podejścia). Tutaj są:

Wyraźnie wymieniając wszystkie znaki akcentowane, które chciałbym zaakceptować jako prawidłowe (kiepskie i zbyt skomplikowane):


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àèìòùÀÈÌÒÙáéíóúýÁÉÍÓÚÝâêîôûÂÊÎÔÛãñõÃÑÕäëïöüÿÄËÏÖÜŸçÇßØøÅåÆæœ]+$/
  • To poprawnie dopasowuje nazwisko / imię do dowolnego z obsługiwanych znaków akcentowanych w accentedCharacters.

Moje inne podejście polegało na użyciu .klasy znaków, aby uzyskać prostsze wyrażenie:

var regex = /^.+,\s.+$/;
  • To pasuje do czegokolwiek, co najmniej w formie: something, something. To chyba w porządku ...

Ostatnie podejście, które właśnie znalazłem, może być prostsze ...

/^[a-zA-Z\u00C0-\u017F]+,\s[a-zA-Z\u00C0-\u017F]+$/
  • Pasuje do wielu znaków Unicode - przetestowanych i działających, chociaż nie próbowałem niczego szalonego, tylko normalne rzeczy, które widzę w naszym dziale językowym dla nazwisk członków wydziału.

Oto moje obawy:

  1. Pierwsze rozwiązanie jest zbyt ograniczone, a do tego niechlujne i zawiłe. Musiałbym to zmienić, gdybym zapomniał o postaci lub dwóch, a to nie jest zbyt praktyczne.
  2. Drugie rozwiązanie jest lepsze, zwięzłe, ale prawdopodobnie pasuje znacznie bardziej niż powinno. Nie mogłem znaleźć żadnej prawdziwej dokumentacji na temat tego , co dokładnie. pasuje, tylko uogólnienie „dowolnego znaku oprócz znaku nowej linii” (z tabeli w MDN ).
  3. Trzecie rozwiązanie wydaje się najbardziej precyzyjne, ale czy są jakieś pułapki? Nie jestem zaznajomiony z Unicode, przynajmniej w praktyce, ale patrząc na tabelę kodów / kontynuację tej tabeli , \u00C0-\u017Fwydaje się być całkiem solidna, przynajmniej jak na mój oczekiwany wkład.

    • Wydział nie będzie przesyłać formularzy z nazwiskami w ich języku ojczystym (np. Arabskim, chińskim, japońskim itp.), Więc nie muszę się martwić o znaki spoza zestawu znaków łacińskich

A więc prawdziwe pytanie (a) : które z tych trzech podejść jest najbardziej odpowiednie do tego zadania? A może są lepsze rozwiązania?

Chris Cirefice
źródło
1
Wydaje się, że nie ma szczególnego powodu, by używać bardziej skomplikowanych wyrażeń regularnych. Jedyną rzeczą w najprostszym rozwiązaniu jest to, że będzie pasowało również „coś, coś, coś”. Możesz użyć czegoś takiego, regex = /^[^,]+,\s[^,]+$/;aby temu zapobiec.
usr2564301
4
Na pierwszy rzut oka pierwsza z nich nie będzie pasować do imienia i nazwiska „O'Donnell, Chris”, ani nie będzie składać się z nazwisk z łącznikiem, ani wielu nazwisk (itp.). Zobacz Fałszywe programiści wierzą w nazwy, aby poznać wszystkie możliwe pułapki.
usr2564301
Atom pasuje niczego poza nowymi liniami ” faktycznie jest dość dokładny :-).
BERGI
1
Jeśli możesz skorzystać z dodatkowej biblioteki, spójrz na moją odpowiedź tutaj
stema
Jongware, właśnie przeczytałem ten artykuł, przeglądając SO w poszukiwaniu odpowiedzi na moje pytanie - zupełnie zapomniałem też o myślnikach i apostrofach i tym podobnych, bardziej zależało mi na tym, aby jako pierwszy był międzynarodowy: P Cieszę się, że to przyniosłeś chociaż! I Stema, faktycznie przyjrzałem się tej bibliotece i unikam włączania bibliotek, ponieważ to wszystko jest w Google Apps Script - dołączanie zewnętrznych bibliotek byłoby koszmarem i używałbym go (w tym przypadku) tylko dla jednego konkretnego pola ... rodzaj przesady: P
Chris Cirefice,

Odpowiedzi:

275

Najłatwiejszym sposobem akceptacji wszystkich akcentów jest:

[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 [ ] ^ \ × ÷

Zobacz https://unicode-table.com/en/, aby znaleźć znaki podane w kolejności numerycznej.

Maycow Moura
źródło
2
Działa ładnie, +1, ale czy możesz wyjaśnić, dlaczego to działa?
Pierre Henry
1
@PierreHenry the -definiuje zakres, a ta technika wykorzystuje kolejność znaków w zestawie znaków w celu zdefiniowania ciągłego zakresu, co zapewnia bardzo zwięzłe rozwiązanie problemu
Angad
8
czy to nie będzie pasowało do podkreślenia (i innych znaków niebędących słowami między Zi a)?
jcuenod
21
To pasuje przynajmniej do znaków [,], ^ i \, z których żaden nie powinien być uwzględniony.
Nate
2
Nie działa, kilka znaków w tym zakresie nie jest znakami akcentowanymi (na przykład U + 00D7 to znak mnożenia) patrz: unicode-table.com/en
Jérémy Pouyet.
39

Akcentowany zakres łaciński \u00C0-\u017Fnie był wystarczający dla mojej bazy danych nazw, więc rozszerzyłem wyrażenie regularne do

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

Dodałem te bloki kodu ( \u00C0-\u024Fzawiera trzy sąsiednie bloki jednocześnie):

Zauważ, że \u00C0-\u00FFjest to właściwie tylko część dodatku Latin-1 . Ten zakres pomija niedrukowalne sygnały sterujące i wszystkie symbole z wyjątkiem niezręcznie umieszczonych mnożenia × \u00D7i dzielenia ÷ \u00F7.

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

Jeśli potrzebujesz więcej punktów kodowych, możesz znaleźć więcej zakresów na liście znaków Unicode w Wikipedii . Na przykład możesz również dodać Latin Extended-C , D i E , ale pominąłem je, ponieważ teraz tylko historycy wydają się nimi zainteresowani, a zestawy D i E nawet nie renderują się poprawnie w mojej przeglądarce.

Oryginalne wyrażenie regularne zatrzymało \u017Fsię na nazwie „Șenol”. Według analizatora Unicode firmy FontSpace , pierwszy znak to \u0218ŁACIŃSKIE WIELKIE LITERY S Z PRZECINKIEM PONIŻEJ. (Tak, zwykle zapisuje się to cedilla-S \u015E, „Şenol”. Ale nie lecę do Turcji, żeby mu powiedzieć: „ Źle wpisujesz swoje imię!”)

Chaim Leib Halbert
źródło
1
Patrząc na blok łaciński tabeli Unicode , myślę, że powinieneś również uwzględnić \ u1e00- \ u1eff, więc robię[a-zA-Z\u00c0-\u024f\u1e00-\u1eff]
cprcrack
18

Które z tych trzech podejść najlepiej nadaje się do tego zadania?

Zależy od zadania :-) Aby dokładnie dopasować wszystkie znaki łacińskie i ich akcentowane wersje, zakresy Unicode prawdopodobnie zapewniają najlepsze rozwiązanie. Można je rozszerzyć na wszystkie znaki niebędące białymi znakami, co można zrobić za pomocą \Sklasy znaków.

Wymuszam pole w interfejsie użytkownika, aby pasowało do formatu: last_name, first_name(najpierw ostatnie [przecinek])

Najbardziej podstawowym problemem, jaki tu widzę, nie są znaki diakrytyczne, ale spacje. Jest kilka nazw, które składają się z wielu słów, np. Tytuły. Powinieneś więc wybrać najbardziej ogólne, czyli zezwalać na wszystko oprócz przecinka, który odróżnia imię od nazwiska:

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

Ale twoje drugie rozwiązanie z .klasą znaków jest równie dobre, możesz wtedy tylko zająć się wieloma przecinkami.

Bergi
źródło
Hm, może masz rację. Prawdopodobnie zbyt skomplikowałem to ... Czy możesz wyjaśnić podane przez Ciebie wyrażenie regularne? Pracuję z regex od jakiegoś czasu, ale tylko z podstawowymi rzeczami i naprawdę nie mam pojęcia, co tak naprawdę robi twoje! Ha
Chris Cirefice,
Jest to klasa znaków zanegowanych - co oznacza „wszystko oprócz przecinka”.
Bergi
Ach, więc czyta się bardziej jak any_character_not_a_comma, any_character_not_a_comma? Tak właśnie pomyślałem, kiedy to przeczytałem po raz pierwszy, trochę się pogubiłem, gdy zobaczyłem tam trzy przecinki.
Chris Cirefice,
Tak, dokładnie. Przepraszam za zamieszanie związane z brakiem sbiałych znaków…
Bergi,
1
@ MateoTibaquirá Możesz uprościć [^\s]do\S
Bergi
15

XRegExp Biblioteka posiada wtyczkę o nazwie Unicode , który pomaga rozwiązywać zadania, takie jak ten.

<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>

Wspomina się o tym w komentarzach do pytania, ale łatwo go przeoczyć. Zauważyłem to dopiero po udzieleniu tej odpowiedzi.

cierń
źródło
Nieźle, okazuje się, że właściwie nie musiałem używać wyrażenia regularnego na Unicode, ale raczej na wzorcu anything, anything.
Przyda się to
12

Co powiesz na to?

/^[a-zA-ZÀ-ÖØ-öø-ÿ]+$/
alchn
źródło
2
Nie pasuje Šš.
Gajus
5

A co z tym?

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

Dopasuje każde słowo ze znakami akcentowanymi lub nie.

Javier Pallarés
źródło
2
Ale OP chce zezwolić na znaki akcentowane.
barbsan
3
/^[\pL\pM\p{Zs}.-]+$/u

Wyjaśnienie:

  • \pL - pasuje do każdego rodzaju listu z dowolnego języka
  • \pM - łączy znak przeznaczony do połączenia z innym znakiem (np. akcenty, umlauty, otaczające pola itp.)
  • \p{Zs} - dopasowuje biały znak, który jest niewidoczny, ale zajmuje miejsce
  • u - Ciągi wzorów i tematów są traktowane jako UTF-8

W przeciwieństwie do innych proponowanych wyrażeń regularnych (takich jak [A-Za-zÀ-ÖØ-öø-ÿ]), będzie to działać ze wszystkimi znakami specyficznymi dla języka, np. ŠšJest dopasowane przez tę regułę, ale nie jest dopasowane przez inne osoby na tej stronie.

Niestety, natywnie JavaScript nie obsługuje tych klas. Możesz jednak użyć xregexpnp

const XRegExp = require('xregexp');

const isInputRealHumanName = (input: string): boolean => {
  return XRegExp('^[\\pL\\pM-]+ [\\pL\\pM-]+$', 'u').test(input);
};
Gajus
źródło