Solución:
Ahora es más fácil que nunca, ya que hay un paquete en nuget llamado Información Unicode
Con esto, puedes simplemente llamar a:
UnicodeInfo.GetName(character)
Aquí hay una solución que puede implementar de inmediato, como copiar / pegar / compilar.
Primero, descargue la base de datos Unicode (UCD) aquí: http://www.unicode.org/Public/UNIDATA/UnicodeData.txt
A continuación, agregue este código a su proyecto para leer el UCD y crear un diccionario para buscar el nombre de un valor de carácter .NET:
string[] unicodedata = File.ReadAllLines( "UnicodeData.txt", Encoding.UTF8 );
Dictionary<char,string> charname_map = new Dictionary<char,string>( 65536 );
for (int i = 0; i < unicodedata.Length; i++)
{
string[] fields = unicodedata[i].Split( ';' );
int char_code = int.Parse( fields[0], NumberStyles.HexNumber );
string char_name = fields[1];
if (char_code >= 0 && char_code <= 0xFFFF) //UTF-16 BMP code points only
{
bool is_range = char_name.EndsWith( ", First>" );
if (is_range) //add all characters within a specified range
{
char_name = char_name.Replace( ", First", String.Empty ); //remove range indicator from name
fields = unicodedata[++i].Split( ';' );
int end_char_code = int.Parse( fields[0], NumberStyles.HexNumber );
if (!fields[1].EndsWith( ", Last>" ))
throw new Exception( "Expected end-of-range indicator." );
for (int code_in_range = char_code; code_in_range <= end_char_code; code_in_range++)
charname_map.Add( (char)code_in_range, char_name );
}
else
charname_map.Add( (char)char_code, char_name );
}
}
El archivo UnicodeData.txt está codificado en UTF-8 y consta de una línea de información para cada punto de código Unicode. Cada línea contiene una lista de campos separados por punto y coma, donde el primer campo es el punto de código Unicode en hexadecimal (sin prefijos) y el segundo campo es el nombre del carácter. La información sobre el archivo y los otros campos que contiene cada línea se puede encontrar aquí: La información sobre el formato del UCD se puede encontrar aquí: http://www.unicode.org/reports/tr44/#Format_Conventions
Una vez que use el código anterior para construir un mapeo de caracteres a los nombres de los personajes, simplemente recupérelos del mapa con algo como esto:
char c="Â";
string character_name;
if (!charname_map.TryGetValue( c, out character_name ))
character_name = "<Character Name Missing>"; //character not found in map
//character_name should now contain "LATIN CAPITAL LETTER A WITH CIRCUMFLEX";
Sugiero incrustar el archivo UnicodeData.txt en los recursos de su aplicación y envolver este código en una clase, que carga y analiza el archivo una vez en un inicializador estático. Para hacer que el código sea más legible, puede implementar un método de extensión en esa clase ‘char’ class como ‘GetUnicodeName’. He restringido deliberadamente los valores al rango de 0 a 0xFFFF, porque eso es todo lo que puede contener un carácter .NET UTF-16. El carácter .NET en realidad no representa un verdadero “carácter” (también llamado punto de código), sino más bien una unidad de código Unicode UTF-16, ya que algunos “caracteres” en realidad requieren dos unidades de código. Este par de unidades de código se denomina sustituto alto y bajo. Los valores superiores a 0xFFFF (el valor más grande que puede almacenar un carácter de 16 bits) están fuera del plano multilingüe básico (BMP) y, de acuerdo con la codificación UTF-16, requieren dos char
s para codificar. Los códigos individuales que forman parte de un par suplente terminarán con nombres como “Suplente alto de uso no privado”, “Suplente alto de uso privado” y “Suplente bajo de uso privado” con esta implementación.
Si usa Process Monitor para ver los archivos a los que accede charmap.exe
, verá que abre un archivo llamado C:Windowssystem32getuname.dll
. Este archivo contiene los nombres de los personajes en sus recursos (en realidad, los recursos mismos están en un archivo .mui en un subdirectorio específico de la cultura).
Entonces, todo lo que tiene que hacer es obtener los nombres de este archivo, usando el LoadString
API. Escribí una clase de ayuda para hacerlo:
public class Win32ResourceReader : IDisposable
{
private IntPtr _hModule;
public Win32ResourceReader(string filename)
{
_hModule = LoadLibraryEx(filename, IntPtr.Zero, LoadLibraryFlags.AsDataFile | LoadLibraryFlags.AsImageResource);
if (_hModule == IntPtr.Zero)
throw Marshal.GetExceptionForHR(Marshal.GetHRForLastWin32Error());
}
public string GetString(uint id)
{
var buffer = new StringBuilder(1024);
LoadString(_hModule, id, buffer, buffer.Capacity);
if (Marshal.GetLastWin32Error() != 0)
throw Marshal.GetExceptionForHR(Marshal.GetHRForLastWin32Error());
return buffer.ToString();
}
~Win32ResourceReader()
{
Dispose(false);
}
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
public void Dispose(bool disposing)
{
if (_hModule != IntPtr.Zero)
FreeLibrary(_hModule);
_hModule = IntPtr.Zero;
}
[DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)]
static extern int LoadString(IntPtr hInstance, uint uID, StringBuilder lpBuffer, int nBufferMax);
[DllImport("kernel32.dll", CharSet = CharSet.Auto, SetLastError = true)]
static extern IntPtr LoadLibraryEx(string lpFileName, IntPtr hReservedNull, LoadLibraryFlags dwFlags);
[DllImport("kernel32.dll", CharSet = CharSet.Auto, SetLastError = true)]
static extern bool FreeLibrary(IntPtr hModule);
[Flags]
enum LoadLibraryFlags : uint
{
AsDataFile = 0x00000002,
AsImageResource = 0x00000020
}
}
Puedes usarlo así:
string path = @"C:WindowsSystem32getuname.dll";
using (var reader = new Win32ResourceReader(path))
{
string name = reader.GetString(0xA9);
Console.WriteLine(name); // Copyright Sign
}