2009-12-07 6 views
13

Không có khả năng quấn quanh đầu tôi cái này là một nguồn thực sự của sự xấu hổ ...C++ trực quan nhân vật Studio mã hóa vấn đề

Tôi đang làm việc với một phiên bản tiếng Pháp của Visual Studio (2008), trong một Pháp Windows (XP). Giọng Pháp đặt trong chuỗi gửi đến cửa sổ đầu ra bị hỏng. Đầu vào Ditto từ cửa sổ đầu ra. Vấn đề mã hóa ký tự điển hình, tôi nhập ANSI, nhận được UTF-8, hoặc một cái gì đó để có hiệu lực đó. Những gì thiết lập có thể đảm bảo rằng các ký tự vẫn còn trong ANSI khi hiển thị một chuỗi "hardcoded" vào cửa sổ đầu ra?

EDIT:

Ví dụ:

#include <iostream> 

int main() 
{ 
std:: cout << "àéêù" << std:: endl; 

return 0; 
} 

sẽ hiển thị trong kết quả:

óúÛ¨

(đây được mã hóa dưới dạng HTML cho niềm vui của bạn)

tôi thực sự muốn hiển thị:

àéêù

+0

Bạn có thể cung cấp cho chúng tôi thêm một chút đầu vào hay không. Điều này có xảy ra cho đầu ra xây dựng, tất cả đầu ra hay cái gì khác không? Bạn có thể cho chúng tôi một hoạt động cụ thể mà điều này xảy ra (xây dựng, gỡ lỗi, v.v ...) – JaredPar

+0

Có, vui lòng hiển thị ví dụ về những gì bạn nghĩ sẽ xuất hiện và những gì thực sự xuất hiện. – wallyk

+1

Điều gì xảy ra nếu bạn sử dụng wcout? – Naveen

Trả lời

13

Trước khi tiếp tục, tôi nên đề cập rằng những gì bạn đang làm không tuân thủ c/C++. Các trạng thái specification trong 2.2 bộ ký tự nào hợp lệ trong mã nguồn. Nó không có nhiều trong đó, và tất cả các nhân vật được sử dụng là trong ascii. Vì vậy, ... Tất cả mọi thứ dưới đây là về một thực hiện cụ thể (như nó xảy ra, VC2008 trên một máy địa phương của Mỹ).

Để bắt đầu, bạn có 4 ký tự trên dòng cout và 4 glyphs trên đầu ra. Vì vậy, vấn đề không phải là một trong những mã hóa UTF8, vì nó sẽ kết hợp nhiều ký tự nguồn để ít glyphs.

Từ bạn nguồn chuỗi để hiển thị trên giao diện điều khiển, tất cả những thứ đó đóng một vai trò:

  1. gì mã hóa tập tin nguồn của bạn là trong (tức là cách C++ tập tin của bạn sẽ được nhìn thấy bởi trình biên dịch)
  2. gì trình biên dịch của bạn không có một chuỗi chữ, và những gì nguồn mã hóa nó hiểu
  3. cách của bạn << giải thích chuỗi mã hóa bạn đang đi qua trong
  4. gì mã hóa các giao diện điều khiển hy vọng
  5. cách giao diện điều khiển dịch đầu ra thành phông chữ.

Bây giờ ...

1 và 2 là những thứ khá dễ dàng. Dường như trình biên dịch đoán định dạng tệp nguồn là gì và giải mã nó thành biểu diễn bên trong của nó. Nó tạo ra chuỗi dữ liệu tương ứng bằng chữ trong bảng mã hiện tại bất kể mã hóa nguồn là gì. Tôi đã không tìm thấy chi tiết/kiểm soát rõ ràng về điều này.

3 thậm chí còn dễ dàng hơn. Ngoại trừ mã kiểm soát, << chỉ chuyển dữ liệu xuống cho char *.

4 được điều khiển bởi SetConsoleOutputCP. Nó sẽ mặc định để mã hóa hệ thống mặc định của bạn. Bạn cũng có thể tìm ra cái nào bạn có với GetConsoleOutputCP (đầu vào được điều khiển khác nhau, thông qua SetConsoleCP)

5 là một điều thú vị. Tôi đập đầu của tôi để tìm ra lý do tại sao tôi không thể có được é để hiển thị đúng cách, sử dụng CP1252 (Tây Âu, cửa sổ). Nó chỉ ra rằng phông chữ hệ thống của tôi không có glyph cho nhân vật đó, và cẩn thận sử dụng glyph của mã chuẩn của tôi (vốn Theta, tôi sẽ nhận được nếu tôi không gọi SetConsoleOutputCP). Để khắc phục nó, tôi đã phải thay đổi phông chữ mà tôi sử dụng trên bảng điều khiển sang Bảng điều khiển Lucida (một phông chữ loại thực).

Một số điều thú vị tôi học kinh nghiệm nhìn này:

  • mã hóa của nguồn không quan trọng, miễn là trình biên dịch có thể hình dung nó ra (đáng chú ý, thay đổi nó để UTF8 không thay đổi mã được tạo Chuỗi "é" của tôi vẫn được mã hóa với CP1252 là 233 0)
  • VC đang chọn một mã mã cho các chuỗi ký tự mà tôi dường như không kiểm soát được.
  • kiểm soát những gì giao diện điều khiển chương trình là đau đớn hơn những gì tôi đã mong

... Vì vậy, điều này có nghĩa đối với bạn? Dưới đây là một số lời khuyên:

  • không sử dụng non-ascii trong chuỗi ký tự chuỗi. Sử dụng tài nguyên, trong đó bạn kiểm soát mã hóa.
  • đảm bảo bạn biết bảng điều khiển của mình mong đợi mã hóa nào và phông chữ của bạn có glyph để thể hiện các ký tự bạn gửi.
  • nếu bạn muốn tìm ra mã hóa nào đang được sử dụng trong trường hợp của bạn, tôi khuyên bạn nên in giá trị thực của ký tự dưới dạng số nguyên. char * a = "é"; std::cout << (unsigned int) (unsigned char) a[0] hiển thị 233 cho tôi, điều này xảy ra là mã hóa trong CP1252.

BTW, nếu bạn nhận được "ÓÚÛ¨" thay vì những gì bạn dán, thì có vẻ như 4 byte của bạn được diễn giải ở đâu đó là CP850.

+0

Sử dụng tài nguyên .. Chắc chắn phải nhìn vào đó. Đây là nơi nó trở nên khó khăn hơn: Giao diện điều khiển hoạt động như một bộ lọc các loại, bởi vì nếu tôi "cin >>" một số chữ có dấu, lo và behold, nhân vật hài hước được nhận ở phía bên kia! Tôi không có mặt ở cỗ máy đó vào lúc này, nhưng tôi sẽ cố gắng reoutput những gì tôi nhận được từ cin và xem nếu nó bị cắt xén hơn nữa hoặc quay trở lại. – MPelletier

+0

Câu trả lời xuất sắc. Tôi chắc chắn sẽ ghi lại điều này. –

+0

Câu trả lời này khá hữu ích để hiểu điều gì sẽ xảy ra với các byte thô của tệp mã nguồn cho một chuỗi chữ thông qua quá trình biên dịch và thông qua hệ thống thời gian chạy. Có lẽ bạn có thể xem http://stackoverflow.com/questions/27871124/does-the-multibyte-to-wide-string-conversion-function-mbstowcs-when-passed-a? –

2

Tôi đã thử mã này:

#include <iostream> 
#include <fstream> 
#include <sstream> 

int main() 
{ 
    std::wstringstream wss; 
    wss << L"àéêù"; 
    std::wstring s = wss.str(); 
    const wchar_t* p = s.c_str(); 
    std::wcout << ws.str() << std::endl; 

    std::wofstream file("C:\\a.txt"); 
    file << p << endl; 

    return 0; 
} 

Các debugger cho thấy WSS, s và p đều có những giá trị mong đợi (ví dụ: "àéêù"), cũng như tập tin đầu ra. Tuy nhiên, những gì xuất hiện trong giao diện điều khiển là óúÛ¨.

Vấn đề là do đó trong bảng điều khiển Visual Studio, không phải C++. Sử dụng câu trả lời tuyệt vời của Bahbar, tôi đã thêm:

SetConsoleOutputCP(1252); 

làm dòng đầu tiên và đầu ra bàn điều khiển xuất hiện như mong muốn.

4

Hãy thử điều này:

#include <iostream> 
#include <locale> 

int main() 
{ 
std::locale::global(std::locale("")); 
std::cout << "àéêù" << std::endl; 

return 0; 
} 
+0

Đẹp, nhưng điều này dường như chỉ làm việc cho đầu ra, đầu vào nhận được từ giao diện điều khiển vẫn còn ngẫu nhiên vô nghĩa. –

0
//Save As Windows 1252 
#include<iostream> 
#include<windows.h> 

int main() 
{ 
    SetConsoleOutputCP(1252); 
    std:: cout << "àéêù" << std:: endl; 
} 

Visual Studio không hỗ trợ UTF 8 cho C++, nhưng một phần hỗ trợ cho C:

//Save As UTF8 without signature 
#include<stdio.h> 
#include<windows.h> 

int main() 
{ 
    SetConsoleOutputCP(65001); 
    printf("àéêù\n"); 
} 
0

Hãy chắc chắn rằng bạn không quên thay đổi các giao diện điều khiển của phông chữ để Lucida Consolas như đề cập bởi Bahbar: nó là rất quan trọng trong trường hợp của tôi (Pháp giành chiến thắng 7 64 bit với VC 2012).Sau đó, như đã đề cập bởi những người khác sử dụng SetConsoleOutputCP (1252) cho C++ nhưng nó có thể thất bại tùy thuộc vào các trang có sẵn, do đó bạn có thể muốn sử dụng GetConsoleOutputCP() để kiểm tra xem nó hoạt động hay ít nhất để kiểm tra xem SetConsoleOutputCP (1252) trả về số không. Thay đổi vị trí toàn cầu cũng làm việc (đối với một số lý do không có nhu cầu để làm cout.imbue (locale());! Nhưng nó có thể phá vỡ một số librairies

Trong C, SetConsoleOutputCP (65001); hoặc locale- cách tiếp cận dựa trên làm việc cho tôi một khi tôi đã cứu mã nguồn như UTF8 mà không cần chữ ký (di chuyển xuống, sự lựa chọn sans-chữ ký là cách dưới đây trong danh sách các trang)

Input sử dụng SetConsoleCP (65001). thất bại cho tôi rõ ràng do một thực hiện xấu của trang 65001 trong cửa sổ.Phương pháp miền địa phương không thành công quá cả trong C và C++. Một giải pháp liên quan hơn, không dựa vào ký tự gốc nhưng trên wchar_t dường như được yêu cầu.

1

Sử dụng _setmode() hoạt động (source) và được cho là tốt hơn thay đổi mã hoặc đặt ngôn ngữ, vì nó thực sự sẽ làm cho chương trình của bạn sử dụng Unicode. Ví dụ:

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

int wmain() 
{ 
    _setmode(_fileno(stdout), _O_U16TEXT); 

    std::wcout << L"àéêù" << std::endl; 

    return 0; 
} 


Bên trong Visual Studio, hãy chắc chắn bạn đã thiết lập dự án của bạn cho Unicode (Kích chuột phải vào Dự án -> Click chung ->Character Set = Sử dụng Unicode Character Set).

người dùng

MinGW:

  1. Xác định cả UNICODE_UNICODE
  2. Thêm -finput-charset=iso-8859-1 đến tùy chọn biên dịch để có được xung quanh lỗi này: "chuyển đổi ký tự thi hành: đối số không hợp lệ"
  3. Thêm -municode vào các tùy chọn liên kết để xem xung quanh "không xác định tham chiếu đến `WinMain @ 16" (read more).