【问题标题】:Reading unicode from console从控制台读取 unicode
【发布时间】:2012-03-19 03:18:17
【问题描述】:

我正在尝试从 C# 中的控制台读取 unicode 字符串,为了举例,让我们使用他的:

c:\SVN\D³ebugger\src\виталик\Program.cs

起初我只是尝试Console.ReadLine(),它返回了我c:\SVN\D3ebugger\src\???????\Program.cs

我尝试将 Console.InputEncoding 设置为 UTF8,就像 Console.InputEncoding = Encoding.UTF8 一样,但它返回给我 c:\SVN\D³ebugger\src\???????\Program.cs,基本上是弄乱了字符串的西里尔字母部分。

所以随机绊倒我试图设置这样的编码,Console.InputEncoding = Encoding.GetEncoding(1251); 返回c:\SVN\D?ebugger\src\виталик\Program.cs,这一次破坏了 ³ 字符。

此时看来,通过为 InputStream 切换encodings,我一次只能获得一种语言。

我也尝试过本地化并做类似的事情:

// Code
public static string ReadLine()
{
    const uint nNumberOfCharsToRead = 1024;
    StringBuilder buffer = new StringBuilder();

    uint charsRead = 0;
    bool result = ReadConsoleW(GetStdHandle(STD_INPUT_HANDLE), buffer, nNumberOfCharsToRead, out charsRead, (IntPtr)0);

    // Return the input minus the newline character
    if (result && charsRead > 1) return buffer.ToString(0, (int)charsRead - 1);
    return string.Empty;
}

// Extern definitions

    [DllImport("Kernel32.DLL", ExactSpelling = true)]
    internal static extern IntPtr GetStdHandle(int nStdHandle);

    [DllImport("kernel32.dll", CharSet = CharSet.Unicode, ExactSpelling = true)]
    static extern bool ReadConsoleW(IntPtr hConsoleInput, [Out] StringBuilder lpBuffer, 
        uint nNumberOfCharsToRead, out uint lpNumberOfCharsRead, IntPtr lpReserved);

这对于非 unicode 字符串运行良好,但是,当我试图让它读取我的示例字符串时,应用程序崩溃了。我试图告诉 Visual Studio 中断所有异常(包括本机异常),但应用程序仍然会崩溃。

我还在 Microsoft 的 Connect 中发现了 this open 错误,这似乎表明现在无法从控制台的 InputStream 中读取 Unicode。

值得注意的是,即使与我的问题没有严格相关,如果 Console.OutputEncoding 设置为 UTF8,Console.WriteLine 能够很好地打印此字符串。

谢谢!

更新 1

我正在寻找 .NET 3.5 的解决方案

更新 2

使用我使用过的完整本机代码进行了更新。

【问题讨论】:

  • 是否可以/可以接受使用命名管道而不是控制台?
  • 如果我找不到解决方案,那我可能会这样做......

标签: c# .net encoding console


【解决方案1】:

当面向 .NET 4 客户端配置文件时,这似乎可以正常工作,但不幸的是,在针对 .NET 3.5 客户端配置文件时却不行。确保将控制台字体更改为 Lucida Console。
正如@jcl 所指出的,即使我的目标是 .NET4,这只是因为我安装了 .NET 4.5。

class Program
{
    private static void Main(string[] args)
    {
        Console.InputEncoding = Encoding.Unicode;
        Console.OutputEncoding = Encoding.Unicode;

        while (true)
        {
            string s = Console.ReadLine();

            if (!string.IsNullOrEmpty(s))
            {
                Debug.WriteLine(s);

                Console.WriteLine(s);
            }
        }
    }
}

【讨论】:

  • 您使用的是 .NET 4.5 吗?它在 .NET 4.0 中不起作用。 Console.InputEncoding = Encoding.Unicode; 行抛出异常:“IOException - 参数不正确。”
  • 我确实安装了 VS 11 beta 和 .NET 4.5 beta。但是,控制台应用程序使用 VS 2010 和 .NET 4 客户端配置文件工作。我使用的是 Windows 7 x64 SP1。
  • 我可以确认我在针对 .NET 3.5 客户端配置文件时遇到了与您相同的异常。
  • 除非您安装了 .NET 4.5,否则它绝对不适用于 .NET 4.0。您的目标应用程序正在使用 mscorlib 的更新版本(奇怪的是,Microsoft 没有在 .NET 4.5 的开发人员预览版中更改版本号,这就是即使您的目标是 4.0 也会使用它的原因),它实际上显式检查Unicode 代码页不调用 SetConsoleCP。检查不包含在 4.0 中的常规 mscorlib.dll 中,这就是它引发 IOException 的原因(当 SetConsoleCP 失败时它会这样做)。
  • 嗨,我正在运行这是一个从属应用程序,它似乎不适合我
【解决方案2】:

这是 .NET 3.5 客户端中的一个完全可用的版本:

class Program
{
  [DllImport("kernel32.dll", SetLastError = true)]
  static extern IntPtr GetStdHandle(int nStdHandle);

  [DllImport("kernel32.dll")]
  static extern bool ReadConsoleW(IntPtr hConsoleInput, [Out] byte[]
     lpBuffer, uint nNumberOfCharsToRead, out uint lpNumberOfCharsRead,
     IntPtr lpReserved);

  public static IntPtr GetWin32InputHandle()
  {
    const int STD_INPUT_HANDLE = -10;
    IntPtr inHandle = GetStdHandle(STD_INPUT_HANDLE);
    return inHandle;
  }

  public static string ReadLine()
  {
    const int bufferSize = 1024;
    var buffer = new byte[bufferSize];

    uint charsRead = 0;

    ReadConsoleW(GetWin32InputHandle(), buffer, bufferSize, out charsRead, (IntPtr)0);
    // -2 to remove ending \n\r
    int nc = ((int)charsRead - 2) * 2;
    var b = new byte[nc];
    for (var i = 0; i < nc; i++)
      b[i] = buffer[i];

    var utf8enc = Encoding.UTF8;
    var unicodeenc = Encoding.Unicode;
    return utf8enc.GetString(Encoding.Convert(unicodeenc, utf8enc, b));
  }

  static void Main(string[] args)
  {
    Console.OutputEncoding = Encoding.UTF8;
    Console.Write("Input: ");
    var st = ReadLine();
    Console.WriteLine("Output: {0}", st);
  }
}

【讨论】:

  • 如果您需要大字符串,请在 ReadLine() 中更改 bufferSize。请注意,缓冲区将占用 两倍 字节作为字符。另外,如果你不介意使用 Linq,你可以使用:var b = buffer.Take(nc).ToArray(); 而不是那个丑陋的 For 循环。
  • 效果很好,谢谢!虽然,我做了一些非常相似的事情(使用 ReadConsoleW),但根本不起作用。我会检查我做错了什么并更新。
  • 之后您可能没有转换为 UTF8。可能输入没问题,但输出不是(只是猜测)
  • 感谢您的回答,我发现了我做错了什么并进行了相应的编辑。它曾经是new StringBuilder(),它在 ANSI 上工作得很好,但在 Unicode 上就崩溃了。现在,当我使用初始大小 - new StringBuilder(nNumberOfCharsToRead) 对其进行初始化时,它工作正常。再次感谢!
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2010-12-28
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多