C# UNICODE 转 ANSI 转换

6

我需要你的帮助,因为在.NET Framework中使用UNICODE编码时,有些问题困扰着我...

我必须与一些非UNICODE应用程序的客户数据系统进行接口交互,而这些客户拥有全球性的公司(例如中国、韩国、俄罗斯等)。所以他们必须提供一个ASCII 8位文件,该文件将使用其Windows代码页进行编码。

因此,如果一个希腊客户发送给我一个包含'Σ'(sigma字母'\u03A3')的产品名称的文本文件,我将得到一个相应于211 ANSI代码点的等效字母,该字母在我的代码页中表示。我的计算机是法语Windows,这意味着代码页是Windows-1252,所以在这个文本文件中我会得到'Ó'...好的。

我知道这个客户是希腊人,所以我可以在导入参数中强制使用windows-1253代码页来读取他的文件。

/// <summary>
/// Convert a string ASCII value using code page encoding to Unicode encoding
/// </summary>
/// <param name="value"></param>
/// <returns></returns>
public static string ToUnicode(string value, int codePage)
{
    Encoding windows = Encoding.Default;
    Encoding unicode = Encoding.Unicode;
    Encoding sp = Encoding.GetEncoding(codePage);
    if (sp != null && !String.IsNullOrEmpty(value))
    {
        // First get bytes in windows encoding
        byte[] wbytes = windows.GetBytes(value);

        // Check if CodePage to use is different from current Windows one
        if (windows.CodePage != sp.CodePage)
        {
            // Convert to Unicode using SP code page
            byte[] ubytes = Encoding.Convert(sp, unicode, wbytes);
            return unicode.GetString(ubytes);
        }
        else
        {
            // Directly convert to Unicode using windows code page
            byte[] ubytes = Encoding.Convert(windows, unicode, wbytes);
            return unicode.GetString(ubytes);
        }
    }
    else
    {
        return value;
    }
}

最终我在申请中获得了'Σ',并能将其保存在我的SQL Server数据库中。现在我的应用程序需要执行一些复杂的计算,然后我必须使用自动导出将该文件返回给客户...
所以我的问题是,我需要进行UNICODE => ANSI转换吗?但这并不像我最初想象的那么简单...
我不想保存导入期间使用的代码页,因此我的第一个想法是将UNICODE转换为windows-1252,然后自动将文件发送给客户。他们将使用自己的代码页阅读导出的文本文件,所以这个想法对我来说很有趣。
但问题是,这种方式的转换行为很奇怪... 这里有两个不同的例子:

第一个例子(я)

char ya = '\u042F';
string strYa = Char.ConvertFromUtf32(ya);
System.Text.Encoding unicode = System.Text.Encoding.Unicode;
System.Text.Encoding ansi1252 = System.Text.Encoding.GetEncoding(1252);
System.Text.Encoding ansi1251 = System.Text.Encoding.GetEncoding(1251);

string strYa1252 = ansi1252.GetString(System.Text.Encoding.Convert(unicode, ansi1252, unicode.GetBytes(strYa)));
string strYa1251 = ansi1251.GetString(System.Text.Encoding.Convert(unicode, ansi1251, unicode.GetBytes(strYa)));

所以strYa1252包含“?”,而strYa1251包含有效字符“я”。因此,如果没有将有效代码页指示给Convert()函数,则似乎无法转换为ANSI ... 因此,Unicode编码类中没有任何内容可以帮助用户获取ANSI和UNICODE代码点之间的等效性? :\

第二个例子(Σ)

char sigma = '\u3A3';
string strSigma = Char.ConvertFromUtf32(sigma);
System.Text.Encoding unicode = System.Text.Encoding.Unicode;
System.Text.Encoding ansi1252 = System.Text.Encoding.GetEncoding(1252);
System.Text.Encoding ansi1253 = System.Text.Encoding.GetEncoding(1253);

string strSigma1252 = ansi1252.GetString(System.Text.Encoding.Convert(unicode, ansi1252, unicode.GetBytes(strSigma)));
string strSigma1253 = ansi1253.GetString(System.Text.Encoding.Convert(unicode, ansi1253, unicode.GetBytes(strSigma)));

此时,strSigma1253 字符串中的 'Σ' 是正确的,但是在 strSigma1252 中却出现了 'S'。正如开头所示,如果找到 ANSI 代码,则应该有 'Ó',如果未找到该字符,则应该有 '?',而不是 'S'。为什么会这样呢?
当然,语言学家可能会说 'S' 和希腊 Sigma 字符相等,因为它们在两个字母表中发音相同,但它们的 ANSI 代码不同!
那么,在 .NET 框架中,Convert() 函数如何处理这种等价性呢?
还有,有人知道如何从 UNICODE 写回 ANSI 字符到我需要发送给客户的文本文件中吗?

2
在将文本转换回客户端的代码页之前,您确实需要知道客户端的代码页。如果您没有这些信息,则无法完成转换。 - Matthew Watson
如果您查看有关Windows-1252代码页的MSDN(例如http://msdn.microsoft.com/en-us/goglobal/cc305145.aspx),则在此页面底部有一个关于ANSI 1252代码和UNICODE代码点之间关系的列表...因此,我认为从UNICODE到一个或多个ANSI代码页时存在等价性?例如,http://www.fileformat.info/info/unicode/char/3a3/charset_support.htm中有所有与所有Windows代码页对应的sigma的代码。 - alex
让您的客户自己使用UTF-8或Unicode可能会更好。您也控制他们使用的软件吗? - Rup
完全不是这样的,这就是问题所在 :P 我们只提供我们的应用程序和他们的应用程序之间的接口,这些应用程序通常是一些旧的“自制”(并且非UNICODE)工业软件....如果解决方案是将客户迁移到一些工业UTF8应用程序,我想我不会发布这个问题^^ 我真的需要通过返回ASCII 8位文件来确保与他们的系统兼容性... - alex
你应该向所有客户询问他们使用的代码页,以建立所需的列表,并询问是否愿意改用UTF-8,如果你认为可以这样做的话。他们只能说不! - Rup
显示剩余2条评论
1个回答

7
我应该使用'?'而不是'S'来表示未找到字符,这被称为“最佳匹配”编码,在大多数情况下并不好。当Windows无法将字符编码为目标代码页(因为代码页1252中不存在Σ)时,它会尽力将字符映射到类似的内容。这可能意味着失去变音符号(ë→e),或映射到一个同源词(Σ→S),一个相关的字符(≤→=),一个不相关但看起来有些相似的字符(∞→8),或者任何其他在实践中被认为具有文化或数学攻击性的疯狂替代方案。你可以在cp1252表格中看到Sigma映射,这里
除了是一个毫无用处的静默混淆之外,它还存在一些相当糟糕的安全隐患。您应该能够通过将EncoderFallback设置为ReplacementFallbackExceptionFallback来阻止其发生。

有人有办法在文本文件中将UNICODE中的ANSI字符写回我必须发送给客户的文件吗?

您需要为每个客户保留编码表。使用该编码对其输入文件进行解码;使用相同的编码编写其输出文件。

(为了保持清晰,请将新客户设置为UTF-8并记录这是首选编码。)


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接