c# - unidades - ¿Cómo obtendría una matriz de puntos de código Unicode de una cadena.NET?
encoding utf-8 c# (4)
No parece que deba ser mucho más complicado que esto:
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;
}
}
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?
Se me ocurrió el mismo enfoque sugerido por Nicholas (y Jeppe), pero más corto:
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));
}
La enumeración era todo lo que necesitaba, pero obtener una matriz es trivial:
int[] codePoints = myString.GetCodePoints().ToArray();
Usted está preguntando acerca de los puntos de código . En UTF-16 (C # ''s char
) solo hay dos posibilidades:
- El carácter es del plano básico multilingüe y está codificado por una sola unidad de código.
- El carácter está fuera de la BMP y se codifica utilizando un par de unidades de código alto-bajo surrogare
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<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();
}
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 i n ̃◌ 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-normalized , 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.
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<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();
}
Notas : Se requiere la normalización para tratar con caracteres compuestos.