使用正则表达式的replace/replaceAll在Unicode问题上的替换

5
有没有一种方法可以在Unicode文本中普遍应用replace方法(这里涉及阿拉伯语)?在下面的示例中,虽然在英语文本中替换整个单词效果很好,但无法检测并因此替换阿拉伯语单词。我添加了u作为标记以启用Unicode解析,但没有帮助。在下面的阿拉伯语示例中,应该替换单词“النجوم”,但不是“والنجوم”,但这并没有发生。
<!DOCTYPE html>
<html>
<body>
<p>Click to replace...</p>
<button onclick="myFunction()">replace</button>
<p id="demo"></p>
<script>
function myFunction() {
  var str = "الشمس والقمر والنجوم، ثم النجوم والنهار";
  var rep = 'النجوم';
  var repWith = 'الليل';

  //var str = "the sun and the stars, then the starsz and the day";
  //var rep = 'stars';
  //var repWith = 'night';

  var result = str.replace(new RegExp("\\b"+rep+"\\b", "ug"), repWith);
  document.getElementById("demo").innerHTML = result;
}
</script>
</body>
</html>

无论您提供什么解决方案,请确保使用变量,就像您在上面的代码中看到的那样(上面的变量rep),因为这些要替换的单词是通过函数调用传递的。

更新:要尝试上面的代码,请将此处的代码替换为上面的代码。


然而,由于您实际上只需要查找阿拉伯字符,因此应该将该正则表达式精确到只包含所需的字符。快速谷歌搜索显示 [\u0621-\u064A\u0660-\u0669 ] 可能有效?不过还没有完全测试和研究... - Tom Lord
这是一个令人讨厌的正则表达式,通过Unicode路线在JS中模拟单词边界。特别是因为JS很糟糕,它不会执行向后查找断言,所以第一个字符必须匹配,并与其右侧紧跟着的前瞻配对...真是太恶心了。我以前做过,它涵盖了所有语言,但你不会喜欢它的。 - user557597
更好的方法是模拟空格边界。 - user557597
谢谢大家的回答。我暂时要离开了,但会测试你们所有的建议。对于最后一个建议,我考虑了一下@sin,但是如果单词在$或^处,空格是不好的。更好的方法是使用indexOf进行测试,并对匹配长度进行比较,以确保完全匹配。 - mohsenmadi
请注意,这是一个单词边界的速记符号 (?:(?:^|(?<=\W))(?=\w)|(?<=\w)(?:$|(?=\W)))。在JS中,这将转换为非常复杂的替换,使用\uDDDD符号,因为JS只知道UTF-16符号,不知道Unicode单词边界是什么。认为这将涵盖所有Unicode是不现实的。我有覆盖所有Unicode的正则表达式,但它很棘手。如果你需要,请告诉我。请注意,[\pL0-9_]并不代表所有Unicode单词字符,它忽略了约3,000个有效单词。 - user557597
显示剩余6条评论
2个回答

3

一个\bword\b模式可以表示为(^|[A-Za-z0-9_])word(?![A-Za-z0-9_])模式,当你需要替换匹配项时,在替换模式前面需要添加$1

由于你需要处理Unicode,因此使用支持“速记”\pL符号的XRegExp库是有意义的。你可以将上述模式中的A-Za-z替换为这个\pL

var str = "الشمس والقمر والنجوم، ثم النجوم والنهار";
var rep = 'النجوم';
var repWith = 'الليل';

var regex = new XRegExp('(^|[^\\pL0-9_])' + rep + '(?![\\pL0-9_])');
var result = XRegExp.replace(str, regex, '$1' + repWith, 'all');
console.log(result);
<script src="https://cdnjs.cloudflare.com/ajax/libs/xregexp/3.1.1/xregexp-all.min.js"></script>

更新by @mohsenmadi:

如果要在Angular应用程序中集成,请按照以下步骤操作:

  1. 运行npm install xregexp以将库添加到package.json中。
  2. 在组件内部,添加import { replace, build } from 'xregexp/xregexp-all.js';
  3. 使用以下代码构建正则表达式:let regex = build('(^|[^\\pL0-9_])' + rep + '(?![\\pL0-9_])');
  4. 使用以下代码替换字符串:let result = replace(str, regex, '$1' + repWith, 'all');

非常感谢这个解决方案!我之前不知道 XRegExp。我刚试了一下,它很好用。我甚至想尝试一个“replaceAll”操作,只需要在 XRegExp.replace() 调用中添加参数 'all',就像 http://xregexp.com/api/#replace 中所示。我需要将这个解决方案集成到 Angular 应用程序中 - 希望一切顺利。我会在进一步研究后接受这个答案。谢谢。 - mohsenmadi

2
如果您改变了对空格边界的想法,这是正则表达式。
var Rx = new RegExp(
   "(^|[\\u0009-\\u000D\\u0020\\u0085\\u00A0\\u1680\\u2000-\\u200A\\u2028-\\u2029\\u202F\\u205F\\u3000])"
   + text +
   "(?![^\\u0009-\\u000D\\u0020\\u0085\\u00A0\\u1680\\u2000-\\u200A\\u2028-\\u2029\\u202F\\u205F\\u3000])"
   ,"ug");

var result = str.replace( Rx, '$1' + repWith );

正则表达式解释

 (                             # (1 start), simulated whitespace boundary
      ^                             # BOL
   |                              # or whitespace
      [\u0009-\u000D\u0020\u0085\u00A0\u1680\u2000-\u200A\u2028-\u2029\u202F\u205F\u3000] 
 )                             # (1 end)

 text                          # To find

 (?!                           # Whitespace boundary
      [^\u0009-\u000D\u0020\u0085\u00A0\u1680\u2000-\u200A\u2028-\u2029\u202F\u205F\u3000] 
 )

在可以使用回顾断言的引擎中,空格边界通常以这种方式完成:(?<!\S)text(?!\S)

谢谢!这也有助于我在构建阿拉伯文本范围方面的其他方面。 - mohsenmadi

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