¿Cómo obtendría una matriz de puntos de código Unicode de una cadena .NET?

Tengo una lista de restricciones de rango de caracteres con las que necesito verificar una cadena, pero el tipo de char en .NET es UTF-16 y, por lo tanto, algunos caracteres se vuelven pares extraños (sustitutos). Por lo tanto, al enumerar todos los caracteres en una string , no obtengo los puntos de código Unicode de 32 bits y algunas comparaciones con valores altos fallan.

Entiendo a Unicode lo suficientemente bien como para analizar los bytes si es necesario, pero estoy buscando una solución BCL para C # / .NET Framework. Asi que …

¿Cómo convertiría una string a una matriz ( int[] ) de puntos de código Unicode de 32 bits?

Esta respuesta no es correcta. Vea la respuesta de @ Virtlink para la correcta.

 static int[] ExtractScalars(string s) { if (!s.IsNormalized()) { s = s.Normalize(); } List chars = new List((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(); } 

Notas : Se requiere la normalización para tratar con caracteres compuestos.

Usted está preguntando acerca de los puntos de código . En UTF-16 (C # ‘s char ) solo hay dos posibilidades:

  1. El carácter es del plano básico multilingüe y está codificado por una sola unidad de código.
  2. El carácter está fuera de la BMP y se codifica utilizando un par de unidades de código alto-bajo de bajo coste.

Por lo tanto, asumiendo que la cadena es válida, esto devuelve una matriz de puntos de código para una cadena dada:

 public static int[] ToCodePoints(string str) { if (str == null) throw new ArgumentNullException("str"); var codePoints = new List(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(); } 

Un ejemplo con un par suplente 🌀 y un carácter compuesto ñ :

 ToCodePoints("\U0001F300 El Ni\u006E\u0303o"); // 🌀 El Niño // { 0x1f300, 0x20, 0x45, 0x6c, 0x20, 0x4e, 0x69, 0x6e, 0x303, 0x6f } // 🌀 E l N in ̃◌ o 

Aquí hay otro ejemplo. Estos dos puntos de código representan una nota musical número 32 con un acento de staccato, ambos pares sustitutos:

 ToCodePoints("\U0001D162\U0001D181"); // 𝅘𝅥𝅰𝆁 // { 0x1d162, 0x1d181 } // 𝅘𝅥𝅰 𝆁◌ 

Cuando C-normaliza , se descomponen en una cabeza de nota, combinando raíz, combinando bandera y combinando acento-staccato, todos los pares sustitutos:

 ToCodePoints("\U0001D162\U0001D181".Normalize()); // 𝅘𝅥𝅰𝆁 // { 0x1d158, 0x1d165, 0x1d170, 0x1d181 } // 𝅘 𝅥 𝅰 𝆁◌ 

Tenga en cuenta que la solución de leppie no es correcta. La pregunta es sobre puntos de código , no elementos de texto . Un elemento de texto es una combinación de puntos de código que juntos forman un solo grafema. Por ejemplo, en el ejemplo anterior, la ñ en la cadena está representada por una minúscula latina n seguida de una tilde combinada ̃◌ . La solución de Leppie descarta cualquier carácter de combinación que no pueda normalizarse en un solo punto de código.

No parece que deba ser mucho más complicado que esto:

 public static IEnumerable Utf32CodePoints( this IEnumerable 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; } } 

Se me ocurrió el mismo enfoque sugerido por Nicholas (y Jeppe), pero más corto:

  public static IEnumerable 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)); } 

La enumeración era todo lo que necesitaba, pero obtener una matriz es trivial:

 int[] codePoints = myString.GetCodePoints().ToArray(); 
    Intereting Posts