2012-03-25 11 views
11

Obecnie piszę aplikację, która wymaga ode mnie wywoływania GetWindowText w dowolnych oknach i zapisywania tych danych do pliku w celu późniejszego przetworzenia. Krótko mówiąc, zauważyłem, że moje narzędzie zostało braku na Battlefield 3 i I zawężony problemu w dół do następnego znaku w tytule okna: http://www.fileformat.info/info/unicode/char/2122/index.htmBłąd wyjścia strumienia w standardzie Windows Unicode C++

Więc stworzyłem małą aplikację testową, która po prostu wykonuje następujące operacje:

std::wcout << L"\u2122"; 

Niski i wygląda, że ​​zrywa wynik z oknem konsoli dla pozostałej części programu.

Dlaczego MSVC STL zadławił się tą postacią (i zakładam, że inni), gdy interfejsy API takie jak MessageBoxW itp. Wyświetlają to dobrze?

Jak mogę uzyskać wydruk tych znaków w moim pliku?

Testowane na VC10 i VC11 pod Windows 7 x64.

Przykro mi z powodu źle skonstruowanego postu, wydzieję sobie tutaj włosy.

Dzięki.

EDIT:

Minimal sprawdzian

#include <fstream> 
#include <iostream> 

int main() 
{ 
    { 
    std::wofstream test_file("test.txt"); 
    test_file << L"\u2122"; 
    } 

    std::wcout << L"\u2122"; 
} 

Oczekiwany wynik: znak '™' wypisywane na konsoli i plików. Obserwowany wynik: Plik został utworzony, ale jest pusty. Brak wyjścia do konsoli.

I potwierdziły, że czcionka I "używam dla mojej konsoli jest zdolny do wyświetlania znaków w pytaniu, a plik jest na pewno pusty (0 bajtów)

EDIT:.

Dalsze debugowanie pokazuje, że „failbit” i „badbit” są ustawione w strumieniu (ów)

EDIT:.

próbowałem również używając Boost.Locale i mam ten sam problem nawet z nowym lokum nasycony globalnie i jawnie do wszystkich stoją strumienie ard.

Odpowiedz

14

Aby zapisać do pliku, trzeba prawidłowo ustawić ustawienia regionalne, na przykład, jeśli chcesz zapisać je jako UTF-8 znaków, trzeba dodać

const std::locale utf8_locale 
      = std::locale(std::locale(), new std::codecvt_utf8<wchar_t>()); 
test_file.imbue(utf8_locale); 

Trzeba dodać te 2 obejmują pliki

#include <codecvt> 
#include <locale> 

aby napisać do konsoli trzeba ustawić konsolę w odpowiednim trybie (jest to specyficzne dla systemu Windows) dodając

_setmode(_fileno(stdout), _O_U8TEXT); 

(w przypadku, gdy chcesz używać UTF-8).

Do tego trzeba dodać te 2 obejmują pliki:

#include <fcntl.h> 
#include <io.h> 

Ponadto musisz upewnić się, że są za pomocą czcionki, która obsługuje Unicode (takich jak na przykład Lucida Console). Możesz zmienić czcionkę we właściwościach okna konsoli.

Cały program wygląda teraz tak:

#include <fstream> 
#include <iostream> 
#include <codecvt> 
#include <locale> 
#include <fcntl.h> 
#include <io.h> 

int main() 
{ 

    const std::locale utf8_locale = std::locale(std::locale(), 
            new std::codecvt_utf8<wchar_t>()); 
    { 
    std::wofstream test_file("c:\\temp\\test.txt"); 
    test_file.imbue(utf8_locale); 
    test_file << L"\u2122"; 
    } 

    _setmode(_fileno(stdout), _O_U8TEXT); 
    std::wcout << L"\u2122"; 
} 
+1

Cóż, będę przeklęty, nasycenie, że locale UTF8 faktycznie działało ... Dlaczego, do cholery, nie jest Boost.Locale, robiąc to dla mnie?Zinterpretowałem dokumenty, że UTF-8 jest domyślnym wąskim kodowaniem, a ja na całym świecie przesadziłem locale i wszystkie statyczne strumienie, więc co do cholery ... – RaptorFactor

2

Czy zawsze używasz std::wcout lub używasz czasem std::cout? Miksowanie tych elementów nie będzie działać. Oczywiście, opis błędu "zadławienie" nie mówi wcale o tym, jaki problem obserwujesz. Podejrzewam jednak, że jest to inny problem niż ten, który używa plików.

Ponieważ nie ma prawdziwego opisu problemu, potrzeba trochę kryształowej kulki, a następnie ujęcia w ciemności, aby uderzyć w problem ... Ponieważ chcesz pobrać znaki Unicode z pliku upewnij się, że strumień plików używasz std::locale, którego aspekt faktycznie przekształca się w odpowiednie kodowanie Unicode.

+0

jestem zawsze za pomocą szerokich typów i API. Nawet coś tak prostego, jak wiersz opublikowany w moim pytaniu nie działa na mojej platformie. To samo, jeśli zastąpisz wcout strumieniem wofstream. – RaptorFactor

+0

Dodałem minimalny przypadek testowy. – RaptorFactor

+0

Czy zweryfikowałeś, że 'std :: codecvt ' używane przez domyślne 'std :: locale' używa kodowania świadomego Unicode? Zwiększenie wydaje się mieć [aspekt UTF-8] (http://www.boost.org/doc/libs/1_49_0/libs/serialization/doc/codecvt.html). Podejrzewałem, że 'std :: tłoczenie' na twojej platformie używa' std :: basic_filebuf 'tj. Działałoby dla obu plików i wymuszało wyjście. –

2

Właśnie przetestowałem GCC (wersje 4.4 do 4.7) i MSVC 10, które wszystkie wykazują ten problem.

Równie uszkodzone jest wprintf, które działa tak samo, jak API strumienia C++.

ja również testowane surowego Win32 API, aby zobaczyć, czy nic innego było przyczyną awarii, a to działa:

#include <windows.h> 
int main() 
{ 
    HANDLE stdout = GetStdHandle(STD_OUTPUT_HANDLE); 
    DWORD n; 
    WriteConsoleW(stdout, L"\u03B2", 1, &n, NULL); 
} 

Który pisze β do konsoli (jeśli czcionki cmd ty ustawiony na coś podobnego Lucida Console) .

Wniosek: wchar_t Dane wyjściowe są okropnie uszkodzone w obu dużych implementacjach bibliotek C++ Standard.

+2

To nie jest strasznie zepsute, tylko okropnie udokumentowane. –

+0

Co byś powiedział, że moje opcje są? Przepis na używanie surowego API wymagałby tysięcy linii kodu. Boost.Locale też nie rozwiązało problemu ... – RaptorFactor

+0

Nie mam podręcznika Nicolaia Josuttisa ["Standardowa biblioteka C++"] (http://www.josuttis.com/libbook/), ale jest określona książka na ten temat. Biorąc pod uwagę, że część IOStreams została napisana przez Dietmara Kühla;), całkiem dobrze oddaje ona cały wątek konwersji postaci w IOStream. – MSalters

1

Chociaż szerokie strumienie znaków pobierają Unicode jako dane wejściowe, nie są tym, co generują jako wynik - znaki przechodzą konwersję. Jeśli znaku nie można przedstawić w kodowaniu, do którego konwertuje, wynik nie powiedzie się.

+0

To wydaje się takie "złe" (z braku lepszego słowa). Nie jestem pewien, czy rozumiem, jak naprawdę pracować/naprawiać to, co mówisz ... – RaptorFactor

+0

Nie sądzę, że to prawda. 'std :: wstringstream' to z pewnością szeroki strumień znaków (dziedziczy po' std :: wstać'), ale nie dokonuje żadnej konwersji. – MSalters