2

我需要将文本字段长度限制为可变数量的字符。我说变量是因为它需要将 CJK 表意文字计为 2 个字符。例如,如果我将长度限制为 10,那么我可以有 10 个拉丁字符但只有 5 个表意文字,或者 4 个拉丁文和 3 个 CJK 表意文字(4 + (3*2))。

我通过使用以下方法在 c# 中很好地实现了这一点:

if (char.GetUnicodeCategory(str, i) == UnicodeCategory.OtherLetter)

问题是这是在表单帖子上检查的,我真正想要的是在用户输入时检查一个 javascript 实现。我可以使用正则表达式来检查每个字符,但我无法找出 UnicodeCategory.OtherLetter 使用了哪些 unicode 块范围。

这个站点似乎对组合正则表达式很有帮助,但我只需要知道我在寻找什么来匹配 c# 实现行为。

4

1 回答 1

4

C#

首先,如果您的目标是仅将 CJK 表意文字计为 2 个字符,那么您拥有的当前 C# 代码并不完全正确。Unicode 通用类别OtherLetter或多或少适用于没有字母大小写概念的脚本。这意味着不仅 CJK 字符会匹配,阿拉伯语、希伯来语、高棉语、格鲁吉亚语等也会匹配。在 Unicode 数据中,CJK 字符被称为汉文字体。

不幸的是,我在 .NET Framework 中找不到简单的解决方案来检查字符的脚本。但是,您可以使用 .NET Regex 来匹配Unicode Blocks。除了一般类别外,只需匹配必要的CJK 块即可。不幸的是,尽管 Unicode 试图保持块的同质性,但它们不能保证来自其他脚本的错误字符最终会出现在“错误”块中。我想这对于 CJK 块来说不太可能。

此外,一个小问题是您可能需要考虑使用System.Globalization.CharUnicodeData.GetUnicodeCategory(str, i)而不是char.GetUnicodeCategory(str, i). 该CharUnicodeData版本旨在与当前版本的 Unicode 保持同步,而另一个可能不是,出于向后兼容性的原因。

JavaScript

不幸的是,JavaScript 的 Unicode 支持并不是那么好,尤其是在正则表达式方面。实际上已经有人问过是否有办法在 JavaScript 中获取一般类别。似乎没有,但那里的答案提到了XRegExp 插件,除了脚本之外,它还可以检查角色的一般类别。

Mathias Bynens 有一篇很棒的文章详细介绍了 JavaScript 当前在 Unicode 方面的缺点以及即将到来的 ECMAScript 6 中的改进。他还提供了指向这些改进的 polyfill 的链接。

尽管 ECMAScript 6 为星光字符提供了更好的支持,但快速浏览一下当前草案(2013 年 10 月 28 日,第 20 版)并没有显示支持匹配 Unicode 通用类别、块或脚本的迹象。

星界人物

星体字符是在基本多语言平面(BMP,平面 0)之外的平面中发现的字符,即值大于 0xFFFF 的字符。C# 和 JavaScript 都使用 UTF-16 作为它们的字符串编码。这意味着字符实际上由 2 个代码单元组成,而不是 BMP 中的 1 个。我对先前 Unicode 问题的回答更详细地介绍了编码,但可以说,这可能会造成严重破坏。特别是,星体字符的字符串长度为 2,正则表达式引擎很难处理它们。

C# 块和 XRegExp 解决方案实际上都不能正确处理星体字符。许多较罕见的 CJK 字符位于补充表意平面(SIP,平面 2)中。也就是说,“字符”是一个重载术语,已被用来表示“代码单元”、“代码点”和“用户感知字符”。对于这个答案,我一直用它来表示code point,但我不知道你指的是哪一个,所以我能做的最好的就是让你意识到星体字符的问题。

请注意,虽然它尚未发布,但 XRegExp 的 GitHub 存储库表明他们已经在即将发布的版本 3 中实现了对星体字符的支持。

手动匹配

考虑到所有困难,最好使用正则表达式手动匹配所有适当的代码点。当然,这样做的缺点是在将新的 CJK 字符添加到标准时必须对其进行更新。CJK 表意文字的码点可以在Unicode 脚本数据中找到,方法是搜索“Han”脚本,然后取Lo(Letter, other) 指示的范围。应该在 C# 和 JavaScript 中工作(尽管未经测试)的相应正则表达式是:

[\u3400-\u4DB5\u4E00-\u9FCC\uF900-\uFA6D\uFA70-\uFAD9]|[\uD840-\uD868][\uDCOO-\uDFFF]|\uD869[\uDC00-\uDED6\uDF00-\uDFFF]|[\uD86A-\uD86C][\uDCOO-\uDFFF]|\uD86D[\uDC00-\uDF34\uDF40-\uDFFF]|\uD86E[\uDCOO-\uDC1D]|\uD87E[\uDC00-\uDE1D]

根据您的定义,代码点 3005、3007、3021-3029、3038-303A、303B 可能被视为表意文字,也可能不被视为表意文字。它们具有“字母,修饰符”和“数字,字母”的Lm类别Nl

于 2013-11-04T09:35:25.643 回答