C# utf 字符串转换,未正确显示的字符被转换为 "unknown character" - 如何防止这种情况发生?
C# utf string conversion, characters which don't display correctly get converted to "unknown character" - how to prevent this?
我有两个源自 Windows 文件名的字符串,其中包含在 Windows 中无法正确显示的 unicode 字符(它们只显示方框“未知字符”,而不是正确的字符)。但是文件名是有效的,并且这些文件在操作系统中没有问题,这意味着我需要能够正确准确地处理它们。
我正在以通常的方式加载文件名:
string path = @"c:\folder";
foreach (FileInfo file in DirectoryInfo.EnumerateFiles(path))
{
string filename = file.FullName;
}
但为了解释这个问题,以下是我遇到问题的两个文件名:
string filename1 = "\ude18.txt";
string filename2 = "\udca6.txt";
两个字符串,两个具有单个 unicode 字符和扩展名的文件名,两者都不同。到目前为止这很好,我可以毫无问题地读写这些文件,但是我需要将这些字符串存储在 sqlite 数据库中,然后再检索它们。我这样做的每一次尝试都会导致这两个字符都被更改为“未知字符”,因此原始数据丢失并且我无法再区分这两个字符串。起初我以为这是一个 sqlite 问题,我已经确定我的数据库是 UTF16,但事实证明是 c# 到 UTF16 的转换导致了问题。
如果我完全忽略 sqlite,并简单地尝试手动将这些字符串转换为 UTF16(或任何其他编码),这些字符将转换为“未知字符”并且原始数据将丢失。如果我这样做:
System.Text.Encoding enc = System.Text.Encoding.Unicode;
string filename1 = "\ude18.txt";
string filename2 = "\udca6.txt";
byte[] name1Bytes = enc.GetBytes(filename1);
byte[] name2Bytes = enc.GetBytes(filename2);
然后我检查字节数组 'name1Bytes' 和 'name2Bytes' 它们都是相同的。我可以看到两种情况下的 unicode 字符都已转换为一对字节 253 和 255 - 未知字符。果然当我转换回来时
string newFilename1 = enc.GetString(name1Bytes);
string newFilename2 = enc.GetString(name2Bytes);
每种情况下的原始 unicode 字符都丢失了,取而代之的是菱形问号符号。我完全丢失了原始文件名。
这些编码转换似乎依赖于能够显示字符的系统字体,这是一个问题,因为这些字符串已经作为文件名存在,并且无法更改文件名。在将其发送到 sqlite 时,我需要以某种方式保留这些数据,当它被发送到 sqlite 时,它将经过一个转换过程到 UTF16,我需要它在不丢失数据的情况下生存的这种转换。
如果您将 char
转换为 int
,您将获得数值,绕过 Unicode 转换机制:
foreach (char ch in filename1)
{
int i = ch; // 0x0000de18 == 56856 for the first char in filename1
... do whatever, e.g., create an int array, store it as base64
}
事实证明这也有效,而且可能更优雅:
foreach (int ch in filename1)
{
...
}
所以也许是这样的:
string Encode(string raw)
{
byte[] bytes = new byte[2 * raw.Length];
int i = 0;
foreach (int ch in raw)
{
bytes[i++] = (byte)(ch & 0xff);
bytes[i++] = (byte)(ch >> 8);
}
return Convert.ToBase64String(bytes);
}
string Decode(string encoded)
{
byte[] bytes = Convert.FromBase64String(encoded);
char[] chars = new char[bytes.Length / 2];
for (int i = 0; i < chars.Length; ++i)
{
chars[i] = (char)(bytes[i * 2] | (bytes[i * 2 + 1] << 8));
}
return new string(chars);
}
我有两个源自 Windows 文件名的字符串,其中包含在 Windows 中无法正确显示的 unicode 字符(它们只显示方框“未知字符”,而不是正确的字符)。但是文件名是有效的,并且这些文件在操作系统中没有问题,这意味着我需要能够正确准确地处理它们。
我正在以通常的方式加载文件名:
string path = @"c:\folder";
foreach (FileInfo file in DirectoryInfo.EnumerateFiles(path))
{
string filename = file.FullName;
}
但为了解释这个问题,以下是我遇到问题的两个文件名:
string filename1 = "\ude18.txt";
string filename2 = "\udca6.txt";
两个字符串,两个具有单个 unicode 字符和扩展名的文件名,两者都不同。到目前为止这很好,我可以毫无问题地读写这些文件,但是我需要将这些字符串存储在 sqlite 数据库中,然后再检索它们。我这样做的每一次尝试都会导致这两个字符都被更改为“未知字符”,因此原始数据丢失并且我无法再区分这两个字符串。起初我以为这是一个 sqlite 问题,我已经确定我的数据库是 UTF16,但事实证明是 c# 到 UTF16 的转换导致了问题。
如果我完全忽略 sqlite,并简单地尝试手动将这些字符串转换为 UTF16(或任何其他编码),这些字符将转换为“未知字符”并且原始数据将丢失。如果我这样做:
System.Text.Encoding enc = System.Text.Encoding.Unicode;
string filename1 = "\ude18.txt";
string filename2 = "\udca6.txt";
byte[] name1Bytes = enc.GetBytes(filename1);
byte[] name2Bytes = enc.GetBytes(filename2);
然后我检查字节数组 'name1Bytes' 和 'name2Bytes' 它们都是相同的。我可以看到两种情况下的 unicode 字符都已转换为一对字节 253 和 255 - 未知字符。果然当我转换回来时
string newFilename1 = enc.GetString(name1Bytes);
string newFilename2 = enc.GetString(name2Bytes);
每种情况下的原始 unicode 字符都丢失了,取而代之的是菱形问号符号。我完全丢失了原始文件名。
这些编码转换似乎依赖于能够显示字符的系统字体,这是一个问题,因为这些字符串已经作为文件名存在,并且无法更改文件名。在将其发送到 sqlite 时,我需要以某种方式保留这些数据,当它被发送到 sqlite 时,它将经过一个转换过程到 UTF16,我需要它在不丢失数据的情况下生存的这种转换。
如果您将 char
转换为 int
,您将获得数值,绕过 Unicode 转换机制:
foreach (char ch in filename1)
{
int i = ch; // 0x0000de18 == 56856 for the first char in filename1
... do whatever, e.g., create an int array, store it as base64
}
事实证明这也有效,而且可能更优雅:
foreach (int ch in filename1)
{
...
}
所以也许是这样的:
string Encode(string raw)
{
byte[] bytes = new byte[2 * raw.Length];
int i = 0;
foreach (int ch in raw)
{
bytes[i++] = (byte)(ch & 0xff);
bytes[i++] = (byte)(ch >> 8);
}
return Convert.ToBase64String(bytes);
}
string Decode(string encoded)
{
byte[] bytes = Convert.FromBase64String(encoded);
char[] chars = new char[bytes.Length / 2];
for (int i = 0; i < chars.Length; ++i)
{
chars[i] = (char)(bytes[i * 2] | (bytes[i * 2 + 1] << 8));
}
return new string(chars);
}