【问题标题】:I need help converting a C# string from one character encoding to another?我需要帮助将 C# 字符串从一种字符编码转换为另一种?
【发布时间】:2011-02-25 06:26:49
【问题描述】:

根据Spolsky我不能称自己为开发人员,所以这个问题背后有很多耻辱......

场景: 在 C# 应用程序中,我想从 SQL 数据库中获取字符串值并将其用作目录的名称。我有一个安全 (SSL) FTP 服务器,我想在其上使用数据库中的字符串值设置当前目录。
问题: 一切正常,直到我用“特殊”字符 - 我似乎无法正确编码目录名称以满足 FTP 服务器的要求。

下面的代码示例

  • 以“特殊”字符é为例
  • 使用 WinSCP 作为 ftps 通信的外部应用程序
  • 未显示设置进程“_winscp”所需的所有代码。
  • 通过写入进程标准输入向 WinSCP exe 发送命令
  • 为简单起见,不从数据库中获取信息,而是简单地声明一个字符串(但我确实做了一个 .Equals 以确认来自数据库的值与声明的字符串相同)
  • 尝试使用不同的字符串编码在 FTP 服务器上设置当前目录 3 次 - 均失败
  • 尝试使用从手工制作的字节数组创建的字符串来设置目录 - 这有效

Process _winscp = new Process();
byte[] buffer;

string nameFromString = "Sinéad O'Connor";
_winscp.StandardInput.WriteLine("cd \"" + nameFromString + "\"");

buffer = Encoding.UTF8.GetBytes(nameFromString);
_winscp.StandardInput.WriteLine("cd \"" + Encoding.UTF8.GetString(buffer) + "\"");

buffer = Encoding.ASCII.GetBytes(nameFromString);
_winscp.StandardInput.WriteLine("cd \"" + Encoding.ASCII.GetString(buffer) + "\"");

byte[] nameFromBytes = new byte[] { 83, 105, 110, 130, 97, 100, 32, 79, 39, 67, 111, 110, 110, 111, 114 };
_winscp.StandardInput.WriteLine("cd \"" + Encoding.Default.GetString(nameFromBytes) + "\"");

UTF8 编码将 é 更改为 101(十进制),但 FTP 服务器不喜欢它。

ASCII 编码将 é 更改为 63(十进制),但 FTP 服务器不喜欢它。

当我将 é 表示为值 130(十进制)时,FTP 服务器很高兴,但我找不到可以为我执行此操作的方法(我必须手动从显式字节构造字符串)。

任何人都知道我应该对我的字符串做些什么来将 é 编码为 130 并使 FTP 服务器满意并最终通过解释开发人员应该理解的唯一一件事将我提升为 1 级开发人员吗?

【问题讨论】:

  • winscp 进程是问题的一部分,它是一个控制台模式应用程序,在代码页 437(旧的 IBM PC 编码)中运行。其中 130 确实是 é 的字符代码。 StandardInput 流通常会自动处理翻译,但您的代码很奇怪。它不能像 sn-p 中给出的那样工作,必须首先启动该过程。失去winscp,使用支持FTP的System.Net。
  • 感谢汉斯提供的信息。我意识到 sn-p 不能按原样工作(我删除了所有进程初始化代码)。我很想使用一些本地 .net FTP 支持 - 但它可以支持 FTP over SSL(即 ftps)吗?
  • 只是为了将来开发人员的完整性——我采纳了 Hans 的建议并查看了本地 .net FTP 库,它们可以处理 ftps——所以我很快切换了代码,现在不再依赖外部WinSCP 应用程序,并且编码似乎没有问题 - 它可以正常工作。很高兴没有外部应用程序。更简单的代码和更好的性能。

标签: c# character-encoding ftps


【解决方案1】:

130 不是 ASCII(ASCII 只有 7 位——参见Encoding.ASCII 文档——所以它把“é”变成了普通的“?”,因为它没有更好的事情可做)。 UTF-8 实际上是将字符编码为 两个字节(十进制:195 和 169),但保留了代码点。

明确地使用代码页,例如Latin (CP 1252)——需要匹配另一边。如下所示,输出中没有“130”,所以...不是您需要的编码 :-) 但同样适用:对特定代码页使用编码。

编辑:正如 Hans Passant 在评论中解释的那样,此处使用的代码页是 MS-DOS (CP 437),这将产生所需的结果。

// LINQPad -- Encoding is System.Text.Encoding
var enc = Encoding.GetEncoding(1252);
string.Join(" ", enc.GetBytes("Sinéad O'Connor")).Dump();
// -> 83 105 110 233 97 100 32 79 39 67 111 110 110 111 114

请参阅:http://msdn.microsoft.com/en-us/goglobal/bb688114 了解更多信息。

编码愉快。

顺便说一句。艺术家的好选择——如果是故意的:p

【讨论】:

  • 感谢 pst 并感谢 Hans。对于那些感兴趣的人,我的代码现在看起来像: string nameFromString = "Sinéad O'Connor"; byte[] buffer = Encoding.GetEncoding(437).GetBytes(nameFromString); _winscp.StandardInput.WriteLine("cd \"" + Encoding.Default.GetString(buffer) + "\"");
【解决方案2】:

我认为这里的问题是所有 .NET 字符串都是 Unicode。 .NET 字符串中没有“我是什么编码”。因此,使用Encoding.ASCII.GetString(buffer) 可以将 ASCII 中的“字符串”转换回 Unicode。

我认为您的问题应该通过更改 Process.StandardInput 的编码来解决,这样您就可以在 WinSCP 中获得正确的编码。

您应该检查Encoding.Default 是什么,因为我很确定它不是 UTF8 或 ASCII。

【讨论】:

  • 谢谢欣快。我确实找到了一种设置 Process.StandardInput 编码的方法,并且我只尝试了 UTF8,但它似乎没有帮助(现在在家没有代码,将在星期一发布)。我不太担心默认编码,因为它只是一种将我的字节数组版本转换为字符串进行测试的方法。
  • @pst:我并不是说字符串根本没有任何编码。我的意思是你不能选择什么编码字符串。它总是 UTF-16。 @Handleman:嗯,现在你可以看到这个“默认”编码既不是 UTF-8 也不是 ASCII,而是你的语言环境编码。
猜你喜欢
  • 1970-01-01
  • 2021-06-16
  • 1970-01-01
  • 1970-01-01
  • 2017-01-08
  • 2021-07-01
  • 2011-04-30
  • 1970-01-01
  • 2013-07-11
相关资源
最近更新 更多