在 .Net 中有没有一种方法可以找出某个字符的 Unicode 名称?
如果没有,是否有一个库可以实现这一点?
这里有一个你可以立即实现的解决方案,就像复制/粘贴/编译一样。
首先,从此处下载Unicode数据库(UCD): http://www.unicode.org/Public/UNIDATA/UnicodeData.txt
接下来,将以下代码添加到您的项目中,以读取UCD并创建Dictionary以查找.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 );
}
}
UnicodeData.txt文件采用UTF-8编码,每个Unicode代码点对应一行信息。每行都包含由分号分隔的字段列表,第一个字段为十六进制的Unicode代码点(没有前缀),第二个字段为字符名称。有关该文件和其他字段的信息可以在此处找到:UCD格式信息可以在此处找到:http://www.unicode.org/reports/tr44/#Format_Conventions
使用上述代码将字符映射到字符名称后,您只需像这样从映射中检索它们即可: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";
我建议将UnicodeData.txt文件嵌入到您的应用程序资源中,并将此代码封装成一个类,该类在静态初始化器中加载和解析文件一次。为了使代码更易读,您可以在该类中实现一个扩展方法'char'类,例如'GetUnicodeName'。我有意将值限制在0到0xFFFF范围内,因为这是.NET UTF-16 char可以容纳的全部内容。.NET char实际上并不表示真正的“字符”(也称为代码点),而是Unicode UTF-16代码单元,因为某些“字符”实际上需要两个代码单元。这样的一对代码单元被称为高代理项和低代理项。超过0xFFFF(16位char可以存储的最大值)的值位于基本多文种平面(BMP)之外,并且根据UTF-16编码需要两个char
来编码。与代理对中的单个代码相关的名称将使用此实现以“非专用使用高代理项”,“专用使用高代理项”和“低代理项”结束。如果您使用“进程监视器”查看charmap.exe
访问的文件,您会发现它打开了一个名为C:\Windows\system32\getuname.dll
的文件。该文件包含其资源中的字符名称(实际上这些资源本身在特定文化子目录中的.mui文件中)。
所以您只需要从这个文件中获取名称,使用LoadString
API即可。我编写了一个帮助类来完成这个任务:
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
}
}
你可以像这样使用它:
string path = @"C:\Windows\System32\getuname.dll";
using (var reader = new Win32ResourceReader(path))
{
string name = reader.GetString(0xA9);
Console.WriteLine(name); // Copyright Sign
}
您可以使用 Windows 自秋季创意者更新(版本 1709 Build 16299) 附带的 u_charName()
ICU API。它适用于非 BMP Unicode 字符(值 > 0xFFFF):
using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.InteropServices;
using System.Text;
using System.Threading.Tasks;
namespace ConsoleApp2
{
internal class Program
{
// u_charName() ICU API that comes with Windows since Fall Creators Update (Version 1709 Build 16299)
// https://docs.microsoft.com/windows/win32/intl/international-components-for-unicode--icu-
// https://unicode-org.github.io/icu-docs/apidoc/released/icu4c/uchar_8h.html#a2d90141097af5ad4b6c37189e7984932
// u_charName() returns the "modern" name of a Unicode character; or the name
// that was defined in Unicode version 1.0, before the Unicode standard merged
// with ISO-10646; or an "extended" name that gives each Unicode code point a
// unique name.
[DllImport("icuuc.dll", CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Unicode, ExactSpelling = true)]
internal extern static int u_charName(int code, UCharNameChoice nameChoice, IntPtr buffer, int bufferLength, out int errorCode);
public enum UCharNameChoice
{
UNICODE_CHAR_NAME,
UNICODE_10_CHAR_NAME,
EXTENDED_CHAR_NAME,
CHAR_NAME_ALIAS,
CHAR_NAME_CHOICE_COUNT
}
private static int CharName(int code, UCharNameChoice nameChoice, out string name)
{
const int nSize = 255;
IntPtr resPtr = Marshal.AllocCoTaskMem(nSize);
try
{
int error;
int nResult = u_charName(code, nameChoice, resPtr, nSize, out error);
if (error != 0)
{
nResult = -1;
name = null;
}
else
name = Marshal.PtrToStringAnsi(resPtr);
return nResult;
}
finally
{
Marshal.FreeCoTaskMem(resPtr);
}
}
private static string FormatUniChar(int ch)
{
string chName;
CharName(ch, UCharNameChoice.EXTENDED_CHAR_NAME, out chName);
return $"U+{ch:X4} {chName}";
}
static void Main(string[] args)
{
string value = "Hello World! ❤️";
List<int> rawUtf32list = new List<int>();
for (int i = 0; i < value.Length; i++)
{
if (Char.IsHighSurrogate(value[i]))
{
rawUtf32list.Add(Char.ConvertToUtf32(value[i], value[i + 1]));
i++;
}
else
rawUtf32list.Add((int)value[i]);
}
Console.WriteLine(value);
foreach (int utf32Char in rawUtf32list)
{
Console.WriteLine(FormatUniChar(utf32Char));
}
}
}
}
打印这个在我的系统上:
U+0048 LATIN CAPITAL LETTER H
U+0065 LATIN SMALL LETTER E
U+006C LATIN SMALL LETTER L
U+006C LATIN SMALL LETTER L
U+006F LATIN SMALL LETTER O
U+0020 SPACE
U+0057 LATIN CAPITAL LETTER W
U+006F LATIN SMALL LETTER O
U+0072 LATIN SMALL LETTER R
U+006C LATIN SMALL LETTER L
U+0064 LATIN SMALL LETTER D
U+0021 EXCLAMATION MARK
U+0020 SPACE
U+1F60D SMILING FACE WITH HEART-SHAPED EYES
U+0020 SPACE
U+2764 HEAVY BLACK HEART
U+FE0F VARIATION SELECTOR-16
正如Hans Passant和MichaelBray所说,.NET没有提供任何内置功能来获取字符的Unicode名称。
您可以使用Unicode字符数据库,该数据库位于http://unicode.org/ucd - 今天它包含所有Unicode 5.2字符(附录#44)的完整信息。
另一种选择是使用Windows中的字符映射表,您可以通过开始\应用程序\附件\系统工具\字符映射表(Win+R => charmap)访问它。
您还可以使用Unicode转换器工具,这是一个开源工具,位于http://unicode.codeplex.com,它还提供了一个用户界面,用于获取信息并从Unicode UCD(附录#44)中使用。此软件的重点是,您可以将此应用程序的EnterpriseAppUnit dll添加到您的应用程序中并使用提供的API。
此程序集包含一些静态方法,它们接受一个'char`并返回名称、十六进制代码、十进制代码等。