【问题标题】:Matching accented characters with Javascript regexes使用 Javascript 正则表达式匹配重音字符
【发布时间】:2023-03-22 10:26:01
【问题描述】:

这是我今天遇到的一个有趣的 sn-p:

/\ba/.test("a") --> true
/\bà/.test("à") --> false

然而,

/à/.test("à") --> true

首先,wtf?

其次,如果我想匹配单词开头的重音字符,我该怎么做? (我真的很想避免使用像/(?:^|\s|'|\(\) .... 这样的过度选择器)

【问题讨论】:

  • 你的 WTF 的答案是 Javascript 不能在正则表达式中正确处理 Unicode。请参阅the standard 以了解它应该如何工作。或者使用在这方面符合标准的语言。仅举几例……在 Perl、PHP、PCRE 和 ICU 正则表达式中,"à" 肯定与模式 /\bà/ 匹配。它们更适合 Unicode 工作。
  • 您可能想要删除重音符号,然后进行简单的 [a-z] 检查。见stackoverflow.com/questions/990904/…

标签: javascript regex unicode internationalization


【解决方案1】:

/\bà/.test("à") 不匹配的原因是因为“à”不是单词字符。转义序列\b 仅在单词字符的边界和非单词字符之间匹配。 /\ba/.test("a") 匹配,因为“a”是一个单词字符。正因为如此,字符串的开头(不是单词字符)和字母“a”之间有一个边界,它是一个单词字符。

JavaScript 正则表达式中的单词字符定义为[a-zA-Z0-9_]

要匹配字符串开头的重音字符,只需在正则表达式的开头使用^ 字符(例如/^à/)。该字符表示字符串的开头(与匹配字符串中任何单词边界的\b 不同)。是最基本、最标准的正则表达式,所以绝对不过分。

【讨论】:

  • 嗯,这解释了很多事情,但我想我在原来的问题中实际上说错了。我需要匹配单词的开头,而不是字符串。我认为选择器“over-the-top”的原因是因为它需要匹配字符串的开头、空格、括号、逗号、句号...
  • +1 我只想用re.test() 方法补充一点,需要注意re.lastIndex 属性的行为,它包含最后一个匹配的偏移量(并且是下一个匹配的位置)比赛尝试将开始)。这在这种情况下不适用,因为该方法被应用于正则表达式文字,但如果正则表达式对象存储在变量中然后多次使用,这确实很重要。
  • Javascript 不符合 The Unicode Standard,因为引用的标准非常清楚地指出,像 à 这样的东西绝对要与正则表达式中的 \w 匹配。
【解决方案2】:

堆栈溢出在正则表达式中也存在非 ASCII 字符的问题,您可以找到它here。他们没有处理单词边界,但可能会给你一些有用的提示。

还有一个page,但他想匹配字符串而不是单词。

我不知道,现在也没有找到解决您问题的锚点,但是当我看到我的第一个链接中使用了哪些怪物正则表达式时,您想要避免的组并没有超出顶部并且我认为你的解决方案。

【讨论】:

    【解决方案3】:

    这对我有用:

    /^[a-z\u00E0-\u00FC]+$/i
    

    here的帮助下

    【讨论】:

    • 使用你的正则表达式你会错过法语字母'ÿ'和'œ'。尝试/^[A-Za-z\u00C0-\u017F]+$/ 获取所有信息。
    • 大写字符的等价物是什么?
    • 从什么时候开始 ÿa 法语字母 :D (这里是母语...)
    • 这个正则表达式包括00F7,即除号
    【解决方案4】:

    const regex = /^[\-/A-Za-z\u00C0-\u017F ]+$/;
    const test1 = regex.test("à");
    const test2 = regex.test("Martinez-Cortez");
    const test3 = regex.test("Leonardo da vinci");
    const test4 = regex.test("ï");
    
    console.log('test1', test1);
    console.log('test2', test2);
    console.log('test3', test3);
    console.log('test4', test4);

    基于 Wak 和 Cœur 的回答:

    /^[\-/A-Za-z\u00C0-\u017F ]+$/

    也适用于空格和破折号。

    例如:莱昂纳多·达·芬奇、马丁内斯-科尔特斯

    【讨论】:

    • 您的示例不包含任何 OP 请求的重音字符
    【解决方案5】:

    Unicode 允许对某些重音字符进行两种替代但等效的表示。例如,é 有两种 Unicode 表示:'\u0039''\u0065\u0301'。前者称为组合形式,后者称为分解形式。 JavaScript 允许在两者之间进行转换:

    'é'.normalize('NFD') // decompose: '\u0039' -> '\u0065\u0301'
    'é'.normalize('NFC') // compose: '\u0065\u0301' -> '\u0039'
    'é'.length // composed form: -> 1
    'é'.length // decomposed form: -> 2 (looks identical but has different representation)
    'é' == 'é' // -> false (composed and decomposed strings are not equal)
    

    代码点'\u0301' 属于Unicode Combining Diacritical Marks 代码块0300-036F。所以匹配这些重音字符的一种方法是以分解的形式比较它们:

    // matching accented characters
    /[a-zA-Z][\u0300-\u036f]+/.test('é'.normalize('NFD')) // -> true
    /\bé/.test('é') // -> false
    /\bé/.test('é'.normalize('NFD')) // -> true (NOTE: /\bé/ uses the decomposed form)
    
    // matching accented words
    /^\w+$/.test('résumé') // -> false
    /^(?:[a-zA-Z][\u0300-\u036f]*)+$/.test('résumé'.normalize('NFD')) // -> true
    

    【讨论】:

      【解决方案6】:

      如果您想匹配字母,无论它们是否带有重音符号,unicode property escapes 都会有所帮助。

      /\p{Letter}*/u.test("à"); // true
      /\p{Letter}/u.test('œ'); // true
      /\p{Letter}/u.test('a'); // true
      /\p{Letter}/u.test('3'); // false
      /\p{Letter}/u.test('a'); // true
      

      匹配单词的开头很棘手,但(?<=(?:^|\s)) 似乎可以解决问题。 (?<= ) 是一个积极的后视,确保在主要表达式之前存在某些东西。 (?: ) 是一个非捕获组,因此在以后使用的任何匹配项中都不会引用此部分。如果未设置多行标志,^ 将匹配字符串的开头;如果设置了多行标志,则\s 将匹配空白字符(空格/制表符/换行符)。

      因此将它们一起使用,它看起来像:

      /(?<=(?:^|\s))\p{Letter}*/u

      如果您想将重音字符匹配到字符串的开头,则需要 a-zA-Z 的否定字符集。

      /(?<=(?:^|\s))[^a-zA-Z]\p{Letter}*/u.match("bœ") // false
      /(?<=(?:^|\s))[^a-zA-Z]\p{Letter}*/u.match("œb") // true
      

      // Match characters, accented or not
      let regex = /\p{Letter}+$/u;
      
      console.log(regex.test("œb")); // true
      console.log(regex.test("bœb")); // true
      console.log(regex.test("àbby")); // true
      console.log(regex.test("à3")); // false
      console.log(regex.test("16 tons")); // true
      console.log(regex.test("3 œ")); // true
      
      console.log('-----');
      
      // Match characters to start of line, only match characters
      
      regex = /(?<=(?:^|\s))\p{Letter}+$/u;
      
      console.log(regex.test("œb")); // true
      console.log(regex.test("bœb")); // true
      console.log(regex.test("àbby")); // true
      console.log(regex.test("à3")); // false
      
      console.log('----');
      
      // Match accented character to start of word, only match characters
      
      regex = /(?<=(?:^|\s))[^a-zA-Z]\p{Letter}+$/u;
      
      console.log(regex.test("œb")); // true
      console.log(regex.test("bœb")); // false
      console.log(regex.test("àbby")); // true
      console.log(regex.test("à3")); // false

      【讨论】:

      • 这是迄今为止最好的答案 - 当前的答案漏掉了很多字母并且包括非字母字符。我添加了指向 MDN 页面的链接。
      猜你喜欢
      • 2015-07-27
      • 2013-07-03
      • 1970-01-01
      • 1970-01-01
      • 2015-10-16
      • 1970-01-01
      • 1970-01-01
      • 2015-07-22
      • 1970-01-01
      相关资源
      最近更新 更多