Regex for names with special characters (Unicode)

前端 未结 7 2204
太阳男子
太阳男子 2020-11-28 09:28

Okay, I have read about regex all day now, and still don\'t understand it properly. What i\'m trying to do is validate a name, but the functions i can find for this on the i

相关标签:
7条回答
  • 2020-11-28 09:58

    When checking your input string you could

    • trim() it to remove leading/trailing whitespaces
    • match against [^\w\s] to detect non-word\non-whitespace characters
    • match against \s+ to get the number of word separators which equals to number of words + 1.

    However I'm not sure that the \w shorthand includes accented characters, but it should fall into "word characters" category.

    0 讨论(0)
  • 2020-11-28 09:59

    Try the following regular expression:

    ^(?:[\p{L}\p{Mn}\p{Pd}\'\x{2019}]+\s[\p{L}\p{Mn}\p{Pd}\'\x{2019}]+\s?)+$
    

    In PHP this translates to:

    if (preg_match('~^(?:[\p{L}\p{Mn}\p{Pd}\'\x{2019}]+\s[\p{L}\p{Mn}\p{Pd}\'\x{2019}]+\s?)+$~u', $name) > 0)
    {
        // valid
    }
    

    You should read it like this:

    ^   # start of subject
        (?:     # match this:
            [           # match a:
                \p{L}       # Unicode letter, or
                \p{Mn}      # Unicode accents, or
                \p{Pd}      # Unicode hyphens, or
                \'          # single quote, or
                \x{2019}    # single quote (alternative)
            ]+              # one or more times
            \s          # any kind of space
            [               #match a:
                \p{L}       # Unicode letter, or
                \p{Mn}      # Unicode accents, or
                \p{Pd}      # Unicode hyphens, or
                \'          # single quote, or
                \x{2019}    # single quote (alternative)
            ]+              # one or more times
            \s?         # any kind of space (0 or more times)
        )+      # one or more times
    $   # end of subject
    

    I honestly don't know how to port this to Javascript, I'm not even sure Javascript supports Unicode properties but in PHP PCRE this seems to work flawlessly @ IDEOne.com:

    $names = array
    (
        'Alix',
        'André Svenson',
        'H4nn3 Andersen',
        'Hans',
        'John Elkjærd',
        'Kristoffer la Cour',
        'Marco d\'Almeida',
        'Martin Henriksen!',
    );
    
    foreach ($names as $name)
    {
        echo sprintf('%s is %s' . "\n", $name, (preg_match('~^(?:[\p{L}\p{Mn}\p{Pd}\'\x{2019}]+\s[\p{L}\p{Mn}\p{Pd}\'\x{2019}]+\s?)+$~u', $name) > 0) ? 'valid' : 'invalid');
    }
    

    I'm sorry I can't help you regarding the Javascript part but probably someone here will.


    Validates:

    • John Elkjærd
    • André Svenson
    • Marco d'Almeida
    • Kristoffer la Cour

    Invalidates:

    • Hans
    • H4nn3 Andersen
    • Martin Henriksen!

    To replace invalid characters, though I'm not sure why you need this, you just need to change it slightly:

    $name = preg_replace('~[^\p{L}\p{Mn}\p{Pd}\'\x{2019}\s]~u', '$1', $name);
    

    Examples:

    • H4nn3 Andersen -> Hnn Andersen
    • Martin Henriksen! -> Martin Henriksen

    Note that you always need to use the u modifier.

    0 讨论(0)
  • 2020-11-28 10:00

    visit this page Unicode Characters in Regular Expression

    0 讨论(0)
  • 2020-11-28 10:06

    Regarding JavaScript it is more tricky, since JavaScript Regex syntax doesn't support unicode character properties. A pragmatic solution would be to match letters like this:

    [a-zA-Z\xC0-\uFFFF]
    

    This allows letters in all languages and excludes numbers and all the special (non-letter) characters commonly found on keyboards. It is imperfect because it also allows unicode special symbols which are not letters, e.g. emoticons, snowman and so on. However, since these symbols are typically not available on keyboards I don't think they will be entered by accident. So depending on your requirements it may be an acceptable solution.

    0 讨论(0)
  • 2020-11-28 10:06

    Here's an optimization over the fantastic answer by @Alix above. It removes the need to define the character class twice, and allows for easier definition of any number of required words.

    ^(?:[\p{L}\p{Mn}\p{Pd}\'\x{2019}]+(?:$|\s+)){2,}$
    

    It can be broken down as follows:

    ^         # start
      (?:       # non-capturing group
        [         # match a:
          \p{L}     # Unicode letter, or
          \p{Mn}    # Unicode accents, or
          \p{Pd}    # Unicode hyphens, or
          \'        # single quote, or
          \x{2019}  # single quote (alternative)
        ]+        # one or more times
        (?:       # non-capturing group
          $         # either end-of-string
        |         # or
          \s+       # one or more spaces
        )         # end of group
      ){2,}     # two or more times
    $         # end-of-string
    

    Essentially, it is saying to find a word as defined by the character class, then either find one or more spaces or an end of a line. The {2,} at the end tells it that a minimum of two words must be found for a match to succeed. This ensures the OP's "Hans" example will not match.


    Lastly, since I found this question while looking for a similar solution for ruby, here is the regular expression as can be used in Ruby 1.9+

    \A(?:[\p{L}\p{Mn}\p{Pd}\'\U+2019]+(?:\Z|\s+)){2,}\Z
    

    The primary changes are using \A and \Z for beginning and end of string (instead of line) and Ruby's Unicode character notation.

    0 讨论(0)
  • 2020-11-28 10:08

    This is the JS regex that I use for fancy names composed with max 3 words (1 to 60 chars), separated by space/single quote/minus sign

    ^([a-zA-Z\xC0-\uFFFF]{1,60}[ \-\']{0,1}){1,3}$
    
    0 讨论(0)
提交回复
热议问题