【问题标题】:C# utf string conversion, characters which don't display correctly get converted to "unknown character" - how to prevent this?C# utf 字符串转换,无法正确显示的字符被转换为“未知字符” - 如何防止这种情况?
【发布时间】:2021-05-30 06:46:07
【问题描述】:

我有两个源自 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 db 中,然后再检索它们。我所做的每一次尝试都会导致这两个字符都被更改为“未知字符”,因此原始数据丢失了,我无法再区分这两个字符串。起初我以为这是一个 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 的转换过程,而正是这种转换,我需要它在不丢失数据的情况下生存。

【问题讨论】:

  • 你为什么认为他们是valid characters
  • 如果Encoding.Unicode.GetBytes 无法获取它,那么它可能一开始就没有正确加载。 SQLite handles Unicode fine如何将这些字符串转换为 C#,请显示代码。
  • 因为它们已经作为文件存在于操作系统中。我没有这样命名它们,它们已经这样存在了
  • 为什么名字前面有个\?
  • 它们是unicode字符,每个字符以\uxxxx的格式显示

标签: c# string sqlite unicode


【解决方案1】:

如果您将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);
}

【讨论】:

  • 这似乎工作得很好,谢谢!正是我想要的解决方案!
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2015-11-14
  • 1970-01-01
  • 1970-01-01
  • 2018-05-07
  • 2019-05-20
相关资源
最近更新 更多