【问题标题】:Encoding UTF8 C# Process编码 UTF8 C# 过程
【发布时间】:2014-04-18 05:56:27
【问题描述】:

我有一个处理 vbscript 并产生输出的应用程序。

private static string processVB(string command, string arguments)
{
    Process Proc = new Process();
    Proc.StartInfo.UseShellExecute = false;
    Proc.StartInfo.RedirectStandardOutput = true;
    Proc.StartInfo.RedirectStandardError = true;
    Proc.StartInfo.RedirectStandardInput = true;
    Proc.StartInfo.StandardOutputEncoding = Encoding.UTF8;
    Proc.StartInfo.StandardErrorEncoding = Encoding.UTF8;
    Proc.StartInfo.FileName = command;
    Proc.StartInfo.Arguments = arguments;
    Proc.StartInfo.WindowStyle = ProcessWindowStyle.Hidden; //prevent console      window from popping up
    Proc.Start();
    string output = Proc.StandardOutput.ReadToEnd();
    string error = Proc.StandardError.ReadToEnd();

    if (String.IsNullOrEmpty(output) && !String.IsNullOrEmpty(error))
    {
        output = error;
    }
    //Console.Write(ping_output);

    Proc.WaitForExit();
    Proc.Close();

    return output;
}

我想我已经正确设置了与 Encoding 属性相关的所有内容。 processVB 方法将获取命令作为 VBscript 文件及其参数。

正在处理该 VBScript 文件的 C# 方法 processVB 现在产生如下输出。

“����?”

但我应该得到原文

"äåéö€"

我已正确设置编码。但我无法做到这一点。

我做错了什么?

【问题讨论】:

  • 你见过这个吗? stackoverflow.com/questions/7520706/… - 我认为这正是你的问题。
  • 是的,我看过。感谢您的参考。不,它没有解决我的问题。
  • 链接解释了问题的根源。
  • CurrentEncoding 属性在启动进程后对 StandardOutput 有什么作用?
  • @cellik: System.Text.UTF8Encoding

标签: c# encoding utf-8 process


【解决方案1】:

此答案没有直接回答问题 - 但我注意到您的代码中可能存在死锁,因此认为无论如何发布它都是值得的。

由于您的代码尝试从重定向的输出中进行同步读取,并且对 StdOut 和 StdErr 都执行此操作,因此存在潜在的死锁。 IE。这部分代码。

Proc.Start();
string output = Proc.StandardOutput.ReadToEnd();
string error = Proc.StandardError.ReadToEnd();

...

Proc.WaitForExit();

可能发生的情况是子进程将大量数据写入 StdErr 并填满缓冲区。一旦缓冲区被填满,子进程将阻塞对 StdErr 的写入(没有发出 StdOut 流结束的信号)。因此孩子被阻止并且不做任何事情,并且您的进程被阻止等待孩子退出。死锁!!!

要解决此问题,应将至少一个(或两个更好)流切换到异步模式。

参见second example in MSDN,专门讨论这个案例场景,以及如何切换到异步模式。

至于UTF-8 的问题,您确定您的子进程是以这种编码输出而不是UTF-16 或其他编码吗?您可能需要检查字节以尝试反转提供的编码流,以便您可以设置正确的编码来解释重定向的流。

编辑

以下是我认为您可以解决编码问题的方法。基本思想基于我曾经需要做的事情 - 我有未知编码的俄语文本,需要弄清楚如何转换它以显示正确的字符 - 获取从 StdOut 捕获的字节,并尝试使用解码它们系统上可用的所有已知代码页。看起来正确的是可能(但不一定) StdOut 编码的编码。即使它看起来与您的数据正确,也不能保证它是一个的原因是因为许多编码在某些字节范围内重叠,这将使其工作相同。例如。在编码基本拉丁字符时,ASCII 和 UTF8 将具有相同的字节。因此,要获得完全匹配,您可能需要发挥创意并使用一些非典型文本进行测试。

这是执行此操作的基本代码 - 可能需要进行调整:

    byte[] text = <put here bytes captured from StandardOut of child process>

    foreach(System.Text.EncodingInfo encodingInfo in System.Text.Encoding.GetEncodings())
    {
        System.Text.Encoding encoding = encodingInfo.GetEncoding();
        string decodedBytes = encoding.GetString(bytes);
        System.Console.Out.WriteLine("Encoding: {0}, Decoded Bytes: {1}", encoding.EncodingName, decodedBytes);
    }

运行代码并手动检查输出。所有与预期文本匹配的都是 StdOut 中使用的编码的候选者。

【讨论】:

  • 非常感谢您指出死锁问题。我会解决的。但 UTF16 或其他编码似乎也不起作用。我已经尝试了一切:(
  • @SeeEM 我刚刚编辑了我的答案(请参阅粗体 EDIT 部分)。我认为这样你应该能够弄清楚使用的编码。
【解决方案2】:

问题是控制台默认不是 UTF-8。它在与 Windows 中的区域设置相同的代码页中运行。解决此问题的一种简单方法是使用chcp 控制台命令。示例:

chcp 65001 && yourScript.vbs

这将导致输出为 UTF-8 格式,并确保您可以从 .NET 应用程序中正确读取它。

请注意,我已经使用bat 脚本而不是 VB 脚本对此进行了测试,但如果 VB 脚本确实支持 UTF-8,它应该可以正常工作。此外,您可能必须显式调用 VB 脚本执行引擎,而不仅仅是 yourScript.vbs。但是你应该能够自己轻松解决这个问题:)

【讨论】:

  • 它似乎不适用于 VBScript。我收到错误“连接到系统的设备无法正常工作”。
  • 我也看到了这个错误。似乎 UTF-8 控制台存在一些问题 - 如果控制台没有支持 UTF-8 的字体(例如 Lucida ConsoleConsolas - 不要使用 Raster Fonts),似乎它根本不起作用,即使您根本不需要控制台对任何东西都是可见的。
  • 根据您对 Mahmoud Al-Qudsi 问题的评论,我尝试通过 AttachConsoleSetConsoleCP 运行该过程。但这似乎不起作用。而且我们不能使用 C# 中的进程直接调用 chcp 65001 && yourScript.vbs 对吧?
  • @SeeEM 好吧,您可以使用chcp 65001 &amp;&amp; yourScript.vbs“命令”启动一个新进程。这可能就足够了。现在,由于您不能使用UseShellExecute,您必须先运行chcp 65001(或使用SetConsoleCP?)然后分别运行您的脚本——&amp;&amp; 操作数是shell 的一部分,而不是“真实”的过程。此外,尝试将控制台的默认字体设置为 ConsolasLucida Console 以查看是否可以解决问题。正如我之前所说,由于某种原因,默认 Raster Fonts 设置对我不起作用。
  • @SeeEM 此外,您应该能够使用SetCurrentConsoleFontEx 以编程方式设置控制台字体。
【解决方案3】:

因为 VBScript 生成的输出是 UTF8

这是给您带来麻烦的假设,它不是 utf-8。也不可能,脚本引擎不支持设置它。您可以自己尝试一下,在示例 .vbs 文件中使用此语句:

 SetLocale 65001

Kaboom,它只接受 LCID 值并且它们不包括 utf 编码。相反,cscript.exe 脚本引擎已经更改了默认代码页本身。它不是默认的 OEM 代码页(HKEY_LOCAL_MACHINE\SYSTEM\ControlSet\Control\Nls\CodePage\OEMCP 值),而是切换到默认的 Windows 代码页。以上记录的注册表项中的 ACP 值。取决于您所在的位置,例如在美洲和西欧将是 1252。

要使用一些 VBScript 代码,请务必使用适合您的语言环境的默认编码保存文件,否则脚本解释器本身会错误解释源代码中的字符串。这本身也可以解释您的问题:

WScript.Echo "Locale: " & GetLocale
WScript.Echo "äåéö€"
WScript.Echo "Changing locale to US-English:"
SetLocale 1033
WScript.Echo "äåéö€"

我的机器上的输出:

C:\temp>cscript test.vbs
Microsoft (R) Windows Script Host Version 5.8
Copyright (C) Microsoft Corporation. All rights reserved.

Locale: 1033
äåéö€
Changing locale to US-English:
äåéö€

所以你的程序中正确的代码行应该是:

Proc.StartInfo.StandardOutputEncoding = Encoding.Default;

请注意,这不是 Process 类使用的默认设置,它将假定控制台模式程序使用 OEM 代码页。就像北美和西欧机器上的 437。您可以在 .vbs 程序中选择另一个 LCID 并更改您的 C# 代码以匹配,但这不是必需的。

请牢记 .vbs 源代码文件编码错误的故障模式。不幸的是,脚本引擎也不支持带有 BOM 的 utf-8。

【讨论】:

  • 我得到了,Locale: 1033 ,+,"? Changing locale to US-English: ,+,"? 当我用Proc.StartInfo.StandardOutputEncoding = Encoding.Default; 执行相同的操作时:(
【解决方案4】:

查看此this answer

可能也和流程输出有关....

【讨论】:

    【解决方案5】:

    另一个进程(vbscript)以某种编码生成和输出。通过设置 StandardOutputEncoding,您可以告诉系统如何读取该流。这不会改变其他进程的编码。

    因此,您需要弄清楚其他进程(VBScript)使用的确切编码。为此,我将直接从 shell 运行脚本并将输出重定向到一个文件并在显示编码的工具中打开它(即 notepad2)如果我是对的,那将是 UTF8 以外的其他东西。

    然后,您将 Proc.StartInfo.StandardOutputEncoding 设置为代码中的该编码,然后一切正常。

    【讨论】:

    • 输出应该用UTF8解析,因为VBScript生成的输出是UTF8。我什至也尝试过其他编码格式。但这没有用!
    【解决方案6】:

    我正在像这样使用你的功能:

    label1.Text = processVB("wscript.exe", "c:\\s.vbs");
    

    我的vbs文件是

    Set fso = CreateObject ("Scripting.FileSystemObject")
    Set stdout = fso.GetStandardStream (1)
    stdout.WriteLine "äåéö€"
    

    我的 vbs 文件被编码为没有 BOM 的 UTF-8

    它按预期工作。我在表单上看到了äåéö€

    也许您应该改变使用函数的方式、vbs 文件的编码方式以及将数据输出到标准输出的方式。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2012-07-26
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2011-11-05
      • 2018-08-28
      • 2022-12-18
      相关资源
      最近更新 更多