Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/csharp/330.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C# 如何从.NET字符串中获取Unicode代码点数组?_C#_String_Unicode_Char_Astral Plane - Fatal编程技术网

C# 如何从.NET字符串中获取Unicode代码点数组?

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

我有一个需要检查字符串的字符范围限制列表,但是.NET中的
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
)中,只有两种可能性:

  • 字符来自基本的多语言平面,由单个代码单元编码
  • 该字符位于BMP之外,并使用代理高-低代码单元对其进行编码
  • 因此,假设字符串有效,这将返回给定字符串的代码点数组:

    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:很明显,伟大的人都有相同的想法:)