C# 如何从.NET字符串中获取Unicode代码点数组?
我有一个需要检查字符串的字符范围限制列表,但是.NET中的C# 如何从.NET字符串中获取Unicode代码点数组?,c#,string,unicode,char,astral-plane,C#,String,Unicode,Char,Astral Plane,我有一个需要检查字符串的字符范围限制列表,但是.NET中的char类型是UTF-16,因此一些字符会变成古怪的(代理)对。因此,当枚举字符串中的所有字符时,我没有得到32位Unicode代码点,并且一些与高值的比较失败 我对Unicode非常了解,如果有必要,我可以自己解析字节,但我正在寻找一个C#/.NET框架BCL解决方案。所以 如何将字符串转换为32位Unicode码点的数组(int[])。请参阅@Virtlink的正确答案。 static int[] ExtractScalars(str
char
类型是UTF-16,因此一些字符会变成古怪的(代理)对。因此,当枚举字符串
中的所有字符
时,我没有得到32位Unicode代码点,并且一些与高值的比较失败
我对Unicode非常了解,如果有必要,我可以自己解析字节,但我正在寻找一个C#/.NET框架BCL解决方案。所以
如何将
字符串
转换为32位Unicode码点的数组(int[]
)。请参阅@Virtlink的正确答案。
static int[] ExtractScalars(string s)
{
if (!s.IsNormalized())
{
s = s.Normalize();
}
List<int> chars = new List<int>((s.Length * 3) / 2);
var ee = StringInfo.GetTextElementEnumerator(s);
while (ee.MoveNext())
{
string e = ee.GetTextElement();
chars.Add(char.ConvertToUtf32(e, 0));
}
return chars.ToArray();
}
static int[]提取标量(字符串s)
{
如果(!s.IsNormalized())
{
s=s.标准化();
}
列表字符=新列表((s.Length*3)/2);
var ee=StringInfo.GetTextElementEnumerator;
while(ee.MoveNext())
{
字符串e=ee.GetTextElement();
chars.Add(char.ConvertToUtf32(e,0));
}
返回chars.ToArray();
}
注意事项:处理复合字符需要标准化。您询问的是代码点。在UTF-16(C#的char
)中,只有两种可能性:
public static int[] ToCodePoints(string str)
{
if (str == null)
throw new ArgumentNullException("str");
var codePoints = new List<int>(str.Length);
for (int i = 0; i < str.Length; i++)
{
codePoints.Add(Char.ConvertToUtf32(str, i));
if (Char.IsHighSurrogate(str[i]))
i += 1;
}
return codePoints.ToArray();
}
publicstaticint[]ToCodePoints(stringstr)
{
如果(str==null)
抛出新的ArgumentNullException(“str”);
var代码点=新列表(str.Length);
对于(int i=0;i
使用代理项对
的示例似乎并不比这复杂得多:
public static IEnumerable<int> Utf32CodePoints( this IEnumerable<char> s )
{
bool useBigEndian = !BitConverter.IsLittleEndian;
Encoding utf32 = new UTF32Encoding( useBigEndian , false , true ) ;
byte[] octets = utf32.GetBytes( s ) ;
for ( int i = 0 ; i < octets.Length ; i+=4 )
{
int codePoint = BitConverter.ToInt32(octets,i);
yield return codePoint;
}
}
公共静态IEnumerable UTF32代码点(此IEnumerable s)
{
bool useBigEndian=!BitConverter.IsLittleEndian;
编码utf32=新的utf32编码(useBigEndian,false,true);
字节[]八位字节=utf32.GetBytes(s);
对于(int i=0;i
我提出了尼古拉斯(和杰普)的建议,只是略短了一点:
public static IEnumerable<int> GetCodePoints(this string s) {
var utf32 = new UTF32Encoding(!BitConverter.IsLittleEndian, false, true);
var bytes = utf32.GetBytes(s);
return Enumerable.Range(0, bytes.Length / 4).Select(i => BitConverter.ToInt32(bytes, i * 4));
}
public static int[] ToCodePoints(string s)
{
byte[] utf32bytes = Encoding.UTF32.GetBytes(s);
int[] codepoints = new int[utf32bytes.Length / 4];
Buffer.BlockCopy(utf32bytes, 0, codepoints, 0, utf32bytes.Length);
return codepoints;
}
此解决方案产生的结果与相同,但略短:
public static IEnumerable<int> GetCodePoints(this string s) {
var utf32 = new UTF32Encoding(!BitConverter.IsLittleEndian, false, true);
var bytes = utf32.GetBytes(s);
return Enumerable.Range(0, bytes.Length / 4).Select(i => BitConverter.ToInt32(bytes, i * 4));
}
public static int[] ToCodePoints(string s)
{
byte[] utf32bytes = Encoding.UTF32.GetBytes(s);
int[] codepoints = new int[utf32bytes.Length / 4];
Buffer.BlockCopy(utf32bytes, 0, codepoints, 0, utf32bytes.Length);
return codepoints;
}
▼: 您的解决方案将丢弃任何修改符,您将处理文本元素而不是代码点。例如,ExtractScalars(“厄尔尼诺\u006E\u0303o”)
转换回字符串的结果将是“厄尔尼诺”
,而不是“厄尔尼诺”
@Virtlink:interest。从文档中看,它一定听起来像是char.ConvertToUtf32(string,int)
应该处理它。编辑:该死的文档声称它应该@Virtlink:好的,它不处理复合字符,但处理代理对。是的,我刚刚研究过。例如,Devanagari音节“ni”是一个可组合字符\u0928\u093F
,规范化后不会变成一个代码点。此外,如果您有一个带有多个修饰符的拉丁字符(例如,^
和~
),则也不会将其规范化为单个代码点。您必须接受您的代码处理文本元素(表示单个图形的代码点组合),并通过执行ConvertToUtf32(e,0)
放弃除第一个代码点之外的所有代码点。无法使用文本元素使代码与代码点一起工作。另一种策略是:var bytes=Encoding.UTF32.GetBytes(s);var int=new int[bytes.Length/4];对于(var idx=0;idx
。当然,您仍然可以先规范化s
。如果需要奇怪的尾数,可以使用newutf32编码(…)
;if(codePoint>0xFFFF)i++而不是Char.ishighbrogate
@CodesInChaos:我相信这是等效的。当且仅当第一个字符是高代理时,您才能获得高于0xFFFF
的代码点,但请告诉我是否我错了。这是等效的。这只是一个风格上的建议。您可能还想在这里添加您的Devanagari音节“ni”示例,即一个由两个代码点组成的单个文本元素,在任何规范化形式下都不会合并到单个代码点。tilde n,ñ
,可以通过(适当的)规范化变成一个代码点。@JeppeStigNielsen I添加了一个示例,其中两个代码点都是代理项对,并在规范化下扩展成四个代码点代理项对。BitConverter
使用本机endianness,编码。UTF32
使用小尾端。所以这将破坏big-endian系统。我只想说,在你提交答案的六秒钟之前,我发布了与leppie的答案相同的解决方案(实际上)作为评论。而且还提到了永恒的烦恼。@JeppeStigNielsen:很明显,伟大的人都有相同的想法:)