我正在尝试做一些文本处理。我可以很容易地为我知道的语言写一个正则表达式(A-Z
为英语),但是在希伯来语,阿拉伯语,中文等中添加一个字母就太多了。
unicode认为哪些字符范围是字母?
除了按其范围分类外,每个Unicode字符都有一个称为"一般类别"的东西。您可能希望使用以下类别:
[Lu] Letter, Uppercase
[Ll] Letter, Lowercase
[Lt] Letter, Titlecase
[Lm] Letter, Modifier
[Lo] Letter, Other
这排除了数字、标点、符号等(它与Java的Character.isLetter
使用的集合相同)。如果你的正则表达式引擎支持它,你可以用p{L}
检查输入字符是否在这些类别之一。
完整的分类列表在Wikipedia上,unicode.org有更多关于p
regex语法的信息。
您可能需要查看这个:Unicode字符范围,其中包含语言的范围。具体来说,没有一个通用的范围,它因语言的不同而不同,比如阿拉伯语是0600 - 06FF,希伯来语是0590 - 05FF等等。
简短回答:
- 如果您的目标
UTF-16
基本多语言平面(BMP):您有380范围。 - 如果您的目标
UTF-16
与补充代码点:您有648范围。
长答:
(如果你想绕过这个解释,直接跳转到代码部分)。
如果我们考虑UTF-16
基本多语言平面(BMP),码点范围从0x0000
到0xFFFF
,这意味着一些65536码点。在Java
或C#
等语言中,Char
支持的类型
除了BMP, Unicode还支持其他平面(实际上是16个平面),以支持其他字符(不知名的语言,表情符号等),范围从0x10000
到0x10FFFF
,使用21位,这基本上是不可能使用UTF-16
的两个字节。这是使用Surrogates
实现的,在BMP范围内的几个代码点(低和高)将帮助我们找到BMP无法表示的字符,在一个特殊的公式中使用低和高的代理来确定目标字符。本文提供更多细节和更好的解释。
让我们把所有这些翻译成语言词汇,如C#
:
BMP
字符用类型Char
表示,可以表示65536个字符。
为了表示其他平面的字符,我们可以使用char.ConvertFromUtf32(n)
(例如n在0x000000
和0x00D7FF
之间,0x00E000
和0x10FFFF
之间,区间[0x00D800
, 0x00DFFF
]专用于代理)。更好的选择是Rune
类型。Rune
类似于Char
,但支持Char
所不能的,这意味着其他平面的代码点。
符文字符"如前所述,不属于Char
范围的部分使用代理计算。换句话说,一个Rune
最多可以由2个Char
实例表示,参见符文。EncodeToUtf16和Rune.Utf16SequenceLength.
被认为是字母的字符范围是什么?
Char
和Rune
都有能力告诉我们一个"字符"是字母还是不是,因为它们都提供了各自的IsLetter()
方法。
一个简单的解决方案是遍历Char
的范围并应用Char。IsLetter方法,同样适用于Rune
。有两种方法:
-
第一个方法
CharLettersRanges
获取的字母范围从0到65535。public IEnumerable<(ulong from, ulong to)> CharLettersRanges() { var ranges = new List<(ulong from, ulong to)>(); char c = default(char); var isLetter = false; ulong start = 0; ulong end = 0x00FFFF; // 65_535; var range = default((ulong from, ulong to)); for (ulong i = start; i <= end; i++) { c = (char)i; if (char.IsLetter(c)) { if (!isLetter) { isLetter = true; range.from = i; } } else { if (isLetter) { isLetter = false; range.to = i - 1; ranges.Add(range); } } } return ranges; }
-
第二种方法
RuneLettersRanges
用于查找超出[ 0,65535 ]的范围(当然是通过避免代理范围),这意味着从0到55,295和从57,344到1,114,111。(请注意,并非所有这些代码点都代表实际的字符,其中许多尚未归属)。public IEnumerable<(ulong from, ulong to)> RuneLettersRanges() { var ranges = new List<(ulong from, ulong to)>(); Rune c = default(Rune); var isLetter = false; var range = default((ulong from, ulong to)); ulong start = 0; ulong end = 0xD7FF; //55_295; for (ulong i = start; i <= end; i++) { c = (Rune)i; if (Rune.IsLetter(c)) { if (!isLetter) { isLetter = true; range.from = i; } } else { if (isLetter) { isLetter = false; range.to = i - 1; ranges.Add(range); } } } //Avoiding surrogates between 0xD800 and 0xDFFF start = 0xE000; //57_344 end = 0x10FFFF; //1_114_111; for (ulong i = start; i <= end; i++) { c = (Rune)i; if (Rune.IsLetter(c)) { if (!isLetter) { isLetter = true; range.from = i; } } else { if (isLetter) { isLetter = false; range.to = i - 1; ranges.Add(range); } } } return ranges; }
第一个方法CharLettersRanges
将返回380个范围,而第二个方法RuneLettersRanges
将返回648个范围。
如果您必须选择一种方法,您可以放心地考虑容量较大的字符Rune
,因为Char
的380个范围包含在Rune
的648个范围中。
为了好玩,你可以计算有多少字母"到现在"在Unicode系统中,通过对范围的长度求和,将得到131756个字母。