283

I've looked on Stack Overflow (replacing characters.. eh, how JavaScript doesn't follow the Unicode standard concerning RegExp, etc.) and haven't really found a concrete answer to the question "How can JavaScript match accented characters (those with diacritical marks)?"

I'm forcing a field in a UI to match the format: last_name, first_name (last [comma space] first), and I want to provide support for diacritics, but evidently in JavaScript it's a bit more difficult than other languages/platforms.

This was my original version, until I wanted to add diacritic support:

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

Currently I'm debating one of three methods to add support, all of which I have tested and work (at least to some extent, I don't really know what the "extent" is of the second approach). Here they are:

Explicitly listing all accented characters that I would want to accept as valid (lame and overly-complicated):


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àèìòùÀÈÌÒÙáéíóúýÁÉÍÓÚÝâêîôûÂÊÎÔÛãñõÃÑÕäëïöüÿÄËÏÖÜŸçÇߨøÅ寿œ]+$/
  • This correctly matches a last/first name with any of the supported accented characters in accentedCharacters.

My other approach was to use the . character class, to have a simpler expression:

var regex = /^.+,\s.+$/;
  • This would match for just about anything, at least in the form of: something, something. That's alright I suppose...

The last approach, which I just found might be simpler...

/^[a-zA-Z\u00C0-\u017F]+,\s[a-zA-Z\u00C0-\u017F]+$/
  • It matches a range of Unicode characters - tested and working, though I didn't try anything crazy, just the normal stuff I see in our language department for faculty member names.

Here are my concerns:

  1. The first solution is far too limiting, and sloppy and convoluted at that. It would need to be changed if I forgot a character or two, and that's just not very practical.

  2. The second solution is better, concise, but it probably matches far more than it actually should. I couldn't find any real documentation on exactly what . matches, just the generalization of "any character except the newline character" (from a table on the MDN).

  3. The third solution seems the be the most precise, but are there any gotchas? I'm not very familiar with Unicode, at least in practice, but looking at a code table/continuation of that table, \u00C0-\u017F seems to be pretty solid, at least for my expected input.

  • Faculty won't be submitting forms with their names in their native language (e.g., Arabic, Chinese, Japanese, etc.), so I don't have to worry about out-of-Latin-character-set characters

Which of these three approaches is most suited for the task? Or are there better solutions?

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
Chris Cirefice
  • 5,475
  • 7
  • 45
  • 75
  • 1
    There seems to be no particular reason to use the more complicated regexps. Only thing about the most simple solution is, it will also match "something, something, something". You could use something like `regex = /^[^,]+,\s[^,]+$/;` to prevent that. – Jongware Dec 19 '13 at 20:53
  • 4
    At a glance, the first one won't match the common name "O'Donnell, Chris" nor compound last names with a hyphen, nor multiple last names (etc.). See [Falsehoods Programmers Believe About Names](http://www.kalzumeus.com/2010/06/17/falsehoods-programmers-believe-about-names/) for just about every possible pitfalls. – Jongware Dec 19 '13 at 20:58
  • 1
    "*the [`.` atom](http://es5.github.io/#x15.10.2.8) matches anything except [newlines](http://es5.github.io/#LineTerminator)*" actually is quite exact :-) – Bergi Dec 19 '13 at 21:22
  • 1
    If it is possible for you to use an additional library you can have a look at my answer [here](http://stackoverflow.com/a/7752816/626273) – stema Dec 19 '13 at 21:40
  • Jongware, I actually just read that article while I was browsing SO for an answer to my question - I also completely forgot about hyphens and apostrophes and the like, I was more concerned with making it international first :P I'm glad you brought it up though! And Stema, I actually looked at that library and I avoid incorporating libraries because this is all on Google Apps Script - incorporating external libraries would be a nightmare, and I would only be using it (in this case) for one particular field... kind of overkill :P – Chris Cirefice Dec 19 '13 at 22:14

10 Answers10

483

The easier way to accept all accents is this:

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

See Unicode Character Table for characters listed in numeric order.

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
Maycow Moura
  • 6,471
  • 2
  • 22
  • 19
  • 4
    It works nicely, +1, but could you elaborate why it works ? – Pierre Henry Jun 15 '16 at 16:07
  • 2
    @PierreHenry the `-` defines a range, and this technique exploits the ordering of characters in the charset to define a continuous range, making for a super concise solution to the problem – Angad Jun 16 '16 at 07:56
  • Thanks. Does it work with Unicode and other Latin charsets (such as iso-8859-1) as well ? (or, is the ordering of the character sthe same across different charsets ?) I think these additional details should be added to the answer since the solution itself is quite elegant imo. – Pierre Henry Jun 16 '16 at 09:00
  • 8
    won't this match underscores (and the other non-word characters between `Z` and `a`)? – jcuenod Aug 11 '16 at 14:24
  • 27
    This matches at least the characters [, ], ^, and \, none of which should be included. – Nate Dec 01 '16 at 16:08
  • 2
    Not working, few characters in this range are not accented characters (U+00D7 is the multiplication sign for example) see this: https://unicode-table.com/en/ – Jérémy Pouyet Jan 24 '17 at 16:27
  • I'm not getting matches for any other characters mentioned above (underscores, slashes, power of sign etc) using value.search(/^[a-zÀ-ÿ \-]+$/i) – Scott Flack Jan 22 '18 at 23:11
  • This is still "works" only for Latin-based languages. Does not work for Chinese or Cyrillic languages. – Illia Ratkevych Feb 15 '18 at 14:58
  • @IlliaRatkevych There are a lot of language characters that can be added. Do you want to add Cyrillic? Use https://unicode-table.com/en/ table to select the ranges and add them to the set. – 1.21 gigawatts Nov 08 '18 at 06:57
  • 1
    Doesn't match `Š`. – Gajus Feb 19 '20 at 09:37
  • what do you mean when you say "includes" / "does not include" `[ ] ^ \ × ÷` - these are math operations not accented letters. – therobyouknow Oct 30 '20 at 12:01
  • Is it because when using `-` as in `À-ÿ` for example, the math characters, `[ ] ^ \ × ÷` , are defined in the character set within that range, even though they are not themselves accented characters using in words. – therobyouknow Oct 30 '20 at 12:03
  • Doesn't match ŐőŰű . – SunWuKung Nov 08 '20 at 19:52
  • this removes Japanese characters - any idea how to include those? – 219CID Aug 13 '21 at 21:14
  • 4
    Reading the comments and seeing all the accented letters that aren't matched, and all the non-letters that are matched, it appears there is no good solution to this problem. – pacoverflow Aug 17 '21 at 06:25
70

The accented Latin range \u00C0-\u017F was not quite enough for my database of names, so I extended the regex to

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

I added these code blocks (\u00C0-\u024F includes three adjacent blocks at once):

Note that \u00C0-\u00FF is actually only a part of Latin-1 Supplement. It skips unprintable control signals and all symbols except for the awkwardly-placed multiply × \u00D7 and divide ÷ \u00F7.

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

If you need more code points, you can find more ranges on Wikipedia's List of Unicode characters. For example, you could also add Latin Extended-C, D, and E, but I left them out because only historians seem interested in them now, and the D and E sets don't even render correctly in my browser.

The original regex stopping at \u017F borked on the name "Șenol". According to FontSpace's Unicode Analyzer, that first character is \u0218, LATIN CAPITAL LETTER S WITH COMMA BELOW. (Yeah, it's usually spelled with a cedilla-S \u015E, "Şenol." But I'm not flying to Turkey to go tell him, "You're spelling your name wrong!")

Chaim Leib Halbert
  • 2,194
  • 20
  • 23
  • 2
    Having a look at the [unicode table latin block](https://en.wikipedia.org/wiki/List_of_Unicode_characters#Latin_Extended_Additional), I think you should also include \u1e00-\u1eff, so I'm doing `[a-zA-Z\u00c0-\u024f\u1e00-\u1eff]` – cprcrack Aug 16 '18 at 10:26
  • 1
    This is the same thing but with glyphs: `[a-zA-ZÀ-ÖÙ-öù-ÿĀ-žḀ-ỿ0-9]`. – Barnee Nov 25 '20 at 21:49
20

Which of these three approaches is most suited for the task?

Depends on the task :-) To match exactly all Latin characters and their accented versions, the Unicode ranges probably provide the best solution. They might be extended to all non-whitespace characters, which could be done using the \S character class.

I'm forcing a field in a UI to match the format: last_name, first_name (last [comma space] first)

The most basic problem I'm seeing here are not diacritics, but whitespaces. There are a few names that consist of multiple words, e.g. for titles. So you should go with the most generic, that is allowing everything but the comma that distinguishes first from last name:

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

But your second solution with the . character class is just as fine, you only might need to care about multiple commata then.

dylankb
  • 1,140
  • 10
  • 14
Bergi
  • 630,263
  • 148
  • 957
  • 1,375
  • Hm, maybe you're right. I probably over-complicated it... Could you explain the regex you provided? I've been working with regex for a little while now, but only basic stuff, and really I don't have a clue what yours actually does! Ha – Chris Cirefice Dec 19 '13 at 21:56
  • It's a [negated character class](http://www.regular-expressions.info/charclass.html#negated) - meaning "anything besides the comma". – Bergi Dec 19 '13 at 22:01
  • Ah, so it reads more like `any_character_not_a_comma, any_character_not_a_comma`? That's what I thought when I first read it, I got kind of confused when I saw three commas in there. – Chris Cirefice Dec 19 '13 at 22:11
  • Yes exactly. Sorry for the confusion with the missing `s` for the whitespace… – Bergi Dec 19 '13 at 22:45
  • Yep, I figured that was supposed to be `\s`, but my first thought was *oh, I wonder what `\\` does*? Haha no big deal, thanks! – Chris Cirefice Dec 19 '13 at 22:48
  • \S is way to permissive for names, it will accet hyphens and such – Phil Apr 27 '17 at 06:28
  • @fdsfdsfdsfds [You've never seen](http://www.kalzumeus.com/2010/06/17/falsehoods-programmers-believe-about-names/) a name with a hyphen? [There are many](https://en.wikipedia.org/wiki/Double-barrelled_name). – Bergi Apr 27 '17 at 08:54
  • I needed to select words with special chars, and thanks to this approach I ended up splitting them with `/\b[^\s]+/g` – Mateo Tibaquira Aug 30 '17 at 02:06
  • 1
    @MateoTibaquirá You can simplify `[^\s]` to `\S` – Bergi Aug 30 '17 at 03:00
17

The XRegExp library has a plugin named Unicode that helps solve tasks like this.

<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>
Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
thorn0
  • 9,362
  • 3
  • 68
  • 96
  • Nice, turns out that I didn't actually need to regex on unicode, but rather on the pattern `anything, anything`. This will be useful for future readers :) – Chris Cirefice Jan 10 '15 at 18:41
15
/^[\pL\pM\p{Zs}.-]+$/u

Explanation:

  • \pL - matches any kind of letter from any language
  • \pM - matches a character intended to be combined with another character (e.g. accents, umlauts, enclosing boxes, etc.)
  • \p{Zs} - matches a whitespace character that is invisible, but does take up space
  • u - Pattern and subject strings are treated as UTF-8

Unlike other proposed regex (such as [A-Za-zÀ-ÖØ-öø-ÿ]), this will work with all language specific characters, e.g. Šš is matched by this rule, but not matched by others on this page.

Unfortunately, natively JavaScript does not support these classes. However, you can use xregexp, e.g.

const XRegExp = require('xregexp');

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

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
Gajus
  • 69,002
  • 70
  • 275
  • 438
  • 6
    This should now work with all JS runtimes supporting [Unicode property escapes](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/RegExp#browser_compatibility)! But you need to tweak it a bit, adding `{}` around `L` and `M`: `/[\p{L}\p{M}\p{Zs}.-]+/gu`. This matches Chinese characters as well, so if you want to only match *Latin* characters with accents, try `/[\p{Script=Latin}\p{M}\p{Zs}.-]+/gu`. For a large table of many useful character categories, check https://javascript.info/regexp-unicode – Ahmed Fasih Jan 10 '22 at 08:14
  • 1
    @AhmedFasih your comment should be the accepted answer, as these escapes seem to now work in all major browsers… – Louis-Rémi Sep 19 '22 at 16:29
12

You can use this:

/^[a-zA-ZÀ-ÖØ-öø-ÿ]+$/
Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
alchn
  • 327
  • 1
  • 5
  • 16
8

You can use this:

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

It will match every word with accented characters or not.

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
Javier Pallarés
  • 321
  • 2
  • 10
8

You can remove the diacritics from alphabets by using:

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

It will remove all the diacritical marks, and then perform your regex on it.

Reference:

Searching and sorting text with diacritical marks in JavaScript

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
Fawaz Ahmed
  • 1,082
  • 2
  • 14
  • 18
  • 3
    I know the OP was asking about regex but this was a solid answer and solved the issue for me. See the current top voted answer question [here](https://stackoverflow.com/questions/18123501/replacing-accented-characters-with-plain-ascii-ones) for a fuller explanation. – bigsee Aug 09 '20 at 09:55
4

From Wikipedia: Basic Latin

For Latin letters, I use

/^[A-zÀ-ÖØ-öø-ÿ]+$/

It avoids hyphens and specials characters.

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
Phil
  • 708
  • 1
  • 11
  • 22
-1

My context is slightly different and limited to French: I want to search text by allowing a mistake of accents.

For example, I want to find "maîtrisée", but the text to be searched is "... maitrisee ...". So, I used the regular expression /ma[i|î|ï]tris[e|é|è|ê|ë]/ in JavaScript.

In the expression, the '[' and ']' define a set of characters, and the '|' is an OR condition.

This page gives a list of accented characters: Diacritiques utilisés en français

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
Nicolas
  • 17
  • 2
  • This is useful for anyone else matching the exact same word in the exact same language, but that's not what this question is about (and it's unlikely anyone else will share this extremely specific requirement of yours). Answers should *directly address* the question, not be orthogonally related, at best. – TylerH Jan 20 '22 at 18:12
  • 1
    You don't need the `|` characters for your use case, you can just use `/ma[iîï]tris[eéèêë]/` – micapam Jun 05 '23 at 04:47