2012-02-29 24 views
12

Próbuję odczytać ciąg Unicode z konsoli w C#, przez wzgląd na przykład, pozwala Uset jego jedno:Czytanie Unicode z konsoli

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

na początku po prostu starał się Console.ReadLine() który wrócił mi c:\SVN\D3ebugger\src\???????\Program.cs

próbowałem ustawić Console.InputEncoding do UTF8 jak tak Console.InputEncoding = Encoding.UTF8 ale wrócił mi c:\SVN\D³ebugger\src\???????\Program.cs zasadniczo niszczeniu cyrylicy część łańcucha .

Tak chaotycznie próbowałem ustawić kodowanie w ten sposób, Console.InputEncoding = Encoding.GetEncoding(1251);, który zwrócił c:\SVN\D?ebugger\src\виталик\Program.cs, tym razem uszkadzając znak ³.

W tym momencie wydaje się, że przełączając encodings dla InputStream, mogę uzyskać tylko jeden język na raz.

Próbowałem zostały również dzieje rodzimych i robi coś takiego:

// 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); 

To działa dobrze na znaki inne niż Unicode, jednak, gdy próbowałem zrobić to przeczytać mój przykładowy ciąg, aplikacja zawieszała . Próbowałem powiedzieć programowi Visual Studio, aby złamał WSZYSTKIE wyjątki (w tym natywne), ale aplikacja nadal ulega awarii.

Znalazłem także this otwarty błąd w Microsoft's Connect, który wydaje się mówić, że nie jest możliwe odczytywanie Unicode z InputStream w konsoli.

Warto zauważyć, choć nie jest to ściśle związane z moim pytaniem, że Console.WriteLine jest w stanie wydrukować ten ciąg dobrze, jeśli Console.OutputEncoding jest ustawione na UTF8.

Dziękujemy!

Update 1

szukam rozwiązania dla .NET 3.5

Aktualizacja 2

Aktualizacja z pełnego kodu natywnego używałem.

+0

Czy jest możliwe/akceptowalne użyć nazwanego potoku zamiast konsoli? – Goyuix

+0

Jeśli nie znajdę rozwiązania, to prawdopodobnie to zrobię ... – VitalyB

Odpowiedz

6

Oto jedna wersja robocza w pełni .NET 3.5 Klient:

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); 
    } 
} 

enter image description here

+0

Zmień wielkość bufora w ReadLine(), jeśli potrzebujesz dużych ciągów. Zauważ, że bufor zajmie ** dwa razy ** tyle bajtów co znaki. Jeśli nie masz nic przeciwko używaniu Linq, możesz użyć: 'var b = buffer.Take (nc) .ToArray();' zamiast tej brzydkiej pętli For. – Jcl

+0

Udało się świetnie, dzięki! Mimo to zrobiłem coś bardzo podobnego (używając ReadConsoleW), które w ogóle nie działałoby. Sprawdzę, co zrobiłem źle i aktualizuję. – VitalyB

+0

Prawdopodobnie nie przekonwertowałeś później na UTF8. Prawdopodobnie dane wejściowe były w porządku, ale wynik nie był (tylko zgadywaniem). – Jcl

10

Wydaje się, że działa prawidłowo podczas kierowania na profil klienta .NET 4, ale niestety nie przy wybieraniu profilu klienta .NET 3.5. Upewnij się, że zmieniasz czcionkę konsoli na Lucida Console.
Jak zauważyłem w @jcl, mimo że celowałem w .NET4, to tylko dlatego, że mam zainstalowany .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); 
      } 
     } 
    } 
} 

enter image description here

+0

Czy jesteś na .NET 4.5, być może? To nie działa w .NET 4.0. Wiersz 'Console.InputEncoding = Encoding.Unicode; 'zgłasza wyjątek:" Wyjątek IOException - parametr jest niepoprawny. " – VitalyB

+0

Mam zainstalowane beta VS 11 i .NET 4.5 beta. Jednak aplikacja konsolowa działa z wykorzystaniem profilu klienta VS 2010 i .NET 4. Używam Windows 7 x64 SP1. – Phil

+0

Mogę potwierdzić, że otrzymuję ten sam wyjątek, co Ty podczas kierowania profilu klienta .NET 3.5. – Phil