带有特殊字符(Unicode)的名称的正则表达式

13

好的,我已经整天阅读关于正则表达式的文章,但仍然没有完全理解。我想要做的是验证一个名称,但我在互联网上找到的函数只使用了[a-zA-Z],留下了我需要接受的字符。

我基本上需要一个正则表达式来检查名称至少为两个单词,并且不包含数字或特殊字符,例如!"#¤%&/()=...,但是单词可以包含像æ、é、Â等字符...

接受的名称示例为:"John Elkjærd" 或 "André Svenson"
不接受的名称示例为:"Hans"、"H4nn3 Andersen" 或 "Martin Henriksen!"

如果有影响,我在客户端使用javascript的.match()函数,并希望仅在服务器端使用php的preg_replace()进行“否定”操作(删除不匹配的字符)。

非常感谢任何帮助。

更新:
好的,感谢Alix Axel's answer,我已经掌握了重要部分,即服务器端。

但是,正如LightWing的回答所示,我无法找到有关JavaScript Unicode支持的任何信息,因此我最终在客户端只得到了一半的解决方案,只检查至少两个单词和最少5个字符的情况:

if(name.match(/\S+/g).length >= minWords && name.length >= 5) {
  //valid
}

另一种方法是按照shifty的答案建议,指定所有unicode字符,我可能会采用类似上面提供的解决方案,但这样有些不切实际。

7个回答

35

尝试使用以下正则表达式:

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

在 PHP 中,这意味着:

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
}

你应该这样阅读:

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

我真的不知道如何将这个转换到JavaScript,我甚至不确定JavaScript是否支持Unicode属性,但在PHP PCRE中,在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');
}

很抱歉我无法帮助你解决Javascript方面的问题,但这里可能会有人可以。


有效性验证:

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

无效性验证:

  • Hans
  • H4nn3 Andersen
  • Martin Henriksen!

要替换无效字符(虽然我不确定你为什么需要这样做),你只需要稍微更改一下:

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

示例:

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

请注意,您始终需要使用u修饰符。


感谢您的答案,这非常完美!现在我只需要让它与JS一起工作,但应该不难,至少现在我有了点思路。 :)哦,我想删除无效字符的原因是为了避免类似“Tamperdata”或“cURL”之类的东西给我带来错误输入,但如果我也进行验证,我猜就没有意义了 :) 再次感谢。 - Kristoffer la Cour
@Kristoffer:我已经更新了我的问题,更好地解释了正则表达式,如果没有其他JS的替代方案,你总是可以使用Ajax并调用PHP来验证它。 - Alix Axel
正则表达式的各个部分解释得非常好,让我不仅仅是盲目地复制粘贴。JS 仍然困扰着我,但如果我找到解决方案,我会在这里发布。 - Kristoffer la Cour
没有找到适用于JS的好解决方案,但我最终按照问题更新中所描述的方式完成了。 - Kristoffer la Cour
@AlixAxel。截至2019年3月11日,js在浏览器中仍然是Unicode的弱项。如您所建议,如果想要实时验证,请使用AJAX;否则,请使用“pattern”属性拒绝主要不需要的代码,然后在服务器上进行完整验证。 - Patanjali

5

关于JavaScript,它更加棘手,因为JavaScript Regex语法不支持Unicode字符属性。一个实用的解决方案是按照以下方式匹配字母:

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

这允许所有语言的字母,但排除键盘常见的数字和所有非字母特殊字符。它并不完美,因为它也允许一些不是字母的Unicode特殊符号,例如表情符号、雪人等等。然而,由于这些符号通常无法在键盘上输入,我认为它们不会被误输入。因此,根据您的需求,这可能是一个可接受的解决方案。


谢谢。我正在寻找这个,因为上面的正则表达式在客户端JavaScript验证中不起作用...最终我得到了这个:public const string NameFull = @"^(?!.{52,})[a-zA-Z\xC0-\uFFFF.'-]{2,50}(?: [a-zA-Z\xC0-\uFFFF.'-]{2,50})+$"; 然后再验证Emoji:https://regex101.com/r/jP5jC5/2 - Yovav


2
这是对上面@Alix非常棒回答的一种优化。它消除了两次定义字符类的需要,并允许更容易地定义所需单词的任意数量。
^(?:[\p{L}\p{Mn}\p{Pd}\'\x{2019}]+(?:$|\s+)){2,}$

它可以分解如下:
^         # 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

基本上,它的意思是查找由字符类定义的单词,然后找到一个或多个空格或行尾。末尾的{2,}告诉它必须至少找到两个单词才能成功匹配。这确保了原作者的 "Hans" 示例不会匹配。
最后,由于我在寻找的类似解决方案时发现了这个问题,因此在Ruby 1.9+中可以使用的正则表达式如下。
\A(?:[\p{L}\p{Mn}\p{Pd}\'\U+2019]+(?:\Z|\s+)){2,}\Z

主要的改变是使用\A和\Z代替行首和行尾,以及Ruby的Unicode字符符号。

2

您可以将允许的特殊字符添加到正则表达式中。

例如:

[a-zA-ZßöäüÖÄÜæé]+

编辑:

这并不是最好的解决方案,但如果至少有两个单词,它将会给出一个结果。

[a-zA-ZßöäüÖÄÜæé]+\s[a-zA-ZßöäüÖÄÜæé]+

1
为什么要使用 [ \t] 而不仅仅是 \s - Alix Axel
2
@Alis:\s更好。谢谢建议。我不是正则表达式专家:D - mjspier

0

在检查输入字符串时,您可以执行以下操作:

  • 使用trim()函数删除前导/尾随空格
  • 使用[^\w\s]匹配非单词/非空格字符
  • 使用\s+匹配单词分隔符的数量,该数量等于单词数+1。

但是我不确定\w速记是否包括重音字符,但它应该属于“单词字符”类别。


\w相当于[0-9a-zA-Z_],具体取决于特定的机器语言环境,这可能(不)适用于重音/ Unicode字符,但无论如何都将匹配数字,而不应该。 - Alix Axel
哦,看来我需要去修复很多我的代码块了 :( 感谢有价值的信息! - ashein

0

这是我用于漂亮名称的JS正则表达式,由最多3个单词(1至60个字符)组成,由空格/单引号/减号分隔

^([a-zA-Z\xC0-\uFFFF]{1,60}[ \-\']{0,1}){1,3}$

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接