2012-03-25 28 views
11

Tôi hiện đang viết một ứng dụng yêu cầu tôi gọi GetWindowText trên các cửa sổ tùy ý và lưu trữ dữ liệu đó vào một tệp để xử lý sau. câu chuyện dài ngắn, tôi nhận thấy rằng công cụ của tôi đã thất bại về Battlefield 3, và tôi thu hẹp vấn đề xuống với nhân vật sau đây trong tiêu đề cửa sổ của nó: http://www.fileformat.info/info/unicode/char/2122/index.htmWindows Unicode C++ Lỗi đầu ra của luồng

Vì vậy, tôi đã tạo ra một ứng dụng thử nghiệm nhỏ mà chỉ làm những điều sau đây:

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

Low and behold ngắt kết xuất ra cửa sổ bảng điều khiển cho phần còn lại của chương trình.

Tại sao MSVC STL nghẹt thở trên ký tự này (và tôi giả định người khác) khi các API như MessageBoxW vv hiển thị nó tốt?

Làm cách nào để có được các ký tự đó được in vào tệp của tôi?

Đã thử nghiệm trên cả VC10 và VC11 trong Windows 7 x64.

Xin lỗi vì bài đăng được xây dựng kém chất lượng, tôi đang xé tóc ra ở đây.

Cảm ơn.

EDIT:

trường hợp thử nghiệm tối thiểu

#include <fstream> 
#include <iostream> 

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

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

Kết quả mong đợi: '™' nhân vật in để an ủi và tập tin. Kết quả quan sát: Tệp được tạo nhưng trống. Không có đầu ra cho bàn điều khiển.

Tôi đã xác nhận rằng phông chữ I "m sử dụng cho giao diện điều khiển của tôi là khả năng hiển thị các nhân vật trong câu hỏi, và các tập tin chắc chắn là rỗng (0 byte trong kích thước)

EDIT:.

Tiếp tục gỡ rối cho thấy 'failbit' và 'badbit' được thiết lập trong dòng (s)

EDIT:.

tôi cũng đã cố gắng sử dụng Boost.Locale và tôi đang gặp vấn đề tương tự ngay cả với ngôn ngữ mới thấm nhuần trên toàn cầu và rõ ràng luồng ard.

Trả lời

14

Để viết vào một tập tin, bạn phải thiết lập miền địa phương một cách chính xác, ví dụ nếu bạn muốn viết chúng như UTF-8 ký tự, bạn phải thêm

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

Bạn cần phải thêm những 2 bao gồm file

#include <codecvt> 
#include <locale> 

để viết ra cửa sổ console bạn phải thiết lập giao diện điều khiển ở chế độ đúng (đây là cửa sổ cụ thể) bằng cách thêm

_setmode(_fileno(stdout), _O_U8TEXT); 

(trong trường hợp bạn muốn sử dụng UTF-8).

Đối với điều này, bạn phải thêm những 2 bao gồm các file:

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

Bên cạnh đó, bạn phải chắc chắn rằng bạn đang sử dụng một font có hỗ trợ Unicode (như ví dụ Lucida Console). Bạn có thể thay đổi phông chữ trong các thuộc tính của cửa sổ bảng điều khiển của mình.

Chương trình hoàn chỉnh bây giờ trông như thế này:

#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

Vâng tôi sẽ bị nguyền rủa, imbuing rằng địa phương UTF8 thực sự làm việc ... Bây giờ tại sao địa ngục không phải là Boost.Locale làm điều đó cho tôi?Tôi giải thích các tài liệu nói rằng UTF-8 được giả định là mã hóa hẹp mặc định, và tôi đã thấm nhuần ngôn ngữ trên toàn cầu và tất cả các luồng tĩnh, vì vậy cái quái gì ... – RaptorFactor

2

Bạn luôn sử dụng std::wcout hoặc đôi khi bạn đang sử dụng std::cout? Trộn chúng sẽ không hoạt động. Tất nhiên, các mô tả lỗi "nghẹt thở" không nói ở tất cả những gì vấn đề bạn đang quan sát. Tôi nghi ngờ rằng đây là một vấn đề khác với một trong những bằng cách sử dụng các tập tin, tuy nhiên.

Vì không có mô tả thực sự về vấn đề phải mất phần nào của quả cầu pha lê, sau đó là ảnh chụp trong bóng tối để khắc phục sự cố ... Vì bạn muốn nhận các ký tự Unicode từ tệp bạn đang sử dụng sử dụng một số std::localestd::codecvt<...> thực sự chuyển đổi thành mã hóa Unicode phù hợp.

+0

Tôi luôn luôn sử dụng các loại rộng và apis. Ngay cả một cái gì đó đơn giản như dòng tôi đăng trong câu hỏi của tôi không thành công trên nền tảng của tôi. Ditto nếu bạn thay thế wcout với một wofstream. – RaptorFactor

+0

Tôi đã thêm một trường hợp thử nghiệm tối thiểu. – RaptorFactor

+0

Bạn đã xác minh rằng 'std :: codecvt ' được sử dụng bởi 'std :: locale' mặc định có sử dụng mã hóa nhận thức Unicode không? Boost dường như có một [khía cạnh UTF-8] (http://www.boost.org/doc/libs/1_49_0/libs/serialization/doc/codecvt.html). Tôi nghi ngờ rằng 'std :: wcout' trên nền tảng của bạn sử dụng' std :: basic_filebuf 'tức là nó sẽ hoạt động cho cả hai tệp và đầu ra consoke. –

2

Tôi vừa thử nghiệm GCC (phiên bản 4.4 đến 4.7) và MSVC 10, tất cả đều thể hiện vấn đề này.

Bị hỏng tương tự là wprintf, ít nhất là API luồng C++.

Tôi cũng đã thử nghiệm Win32 API liệu để xem nếu không có gì khác đã gây ra thất bại, và các công trình này:

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

nào viết β ra cửa sổ Console (nếu bạn thiết lập phông chữ của cmd để cái gì đó như Lucida Console) .

Kết luận: wchar_t đầu ra bị phá vỡ khủng khiếp trong cả triển khai thư viện chuẩn C++ lớn.

+2

Nó không phải là khủng khiếp bị hỏng, chỉ tài liệu khủng khiếp. –

+0

Bạn sẽ nói gì về các lựa chọn của tôi? Việc viết lại để sử dụng API thô sẽ liên quan đến hàng nghìn dòng mã. Boost.Locale dường như không giải quyết được vấn đề ... – RaptorFactor

+0

Tôi không có Nicolai Josuttis '[Thư viện chuẩn C++'] (http://www.josuttis.com/libbook/) tiện dụng, nhưng nó cuốn sách xác định về chủ đề. Và xem xét rằng các bit IOStreams là đồng viết bởi Dietmar Kühl;), nó bao gồm các công cụ chuyển đổi toàn bộ nhân vật trong IOStream khá tốt. – MSalters

1

Mặc dù các dòng ký tự rộng lấy Unicode làm đầu vào, nhưng đó không phải là những gì chúng tạo ra dưới dạng đầu ra - các ký tự trải qua một chuyển đổi. Nếu một ký tự không thể được biểu diễn trong mã hóa mà nó chuyển đổi thành, thì đầu ra không thành công.

+0

Điều đó có vẻ như 'sai' (vì thiếu từ tốt hơn). Tôi không chắc tôi hiểu làm thế nào để thực sự làm việc xung quanh/sửa chữa những gì bạn đang nói mặc dù ... – RaptorFactor

+0

Tôi không nghĩ rằng đó là sự thật, một trong hai. 'std :: wstringstream' chắc chắn là một luồng ký tự rộng (thừa kế từ' std :: wstream'), nhưng không thực hiện bất kỳ chuyển đổi nào. – MSalters

Các vấn đề liên quan