2013-04-05 61 views
8

Cách tốt nhất để đặt mã hóa trong C++ là gì?Cách nào đúng nhất để đặt mã hóa trong C++?

Tôi đã từng làm việc với Unicode (và wchar_t, wstring, wcin, wcout và L "..."). Tôi cũng tiết kiệm nguồn bằng UTF-8.

Hiện tại tôi sử dụng MinGW (Windows 7) và chạy chương trình trong giao diện điều khiển Windows (cmd.exe), nhưng đôi khi tôi có thể sử dụng gcc trên GNU \ Linux và chạy chương trình trong bảng điều khiển Linux với mã hóa UTF-8.

Tại mọi thời điểm tôi muốn biên dịch nguồn của mình trên Windows và trên Linux và tôi muốn tất cả các ký hiệu Unicode được nhập chính xác và xuất ra.

Khi tôi gặp vấn đề tiếp theo với mã hóa, tôi đã googled. Ngoài ra tôi thấy các hội đồng khác nhau nhất: setlocale(LC_ALL, "")setlocale(LC_ALL, "xx_XX.UTF-8"), std::setlocale(LC_ALL, "")std::setlocale(LC_ALL, "xx_XX.UTF-8") từ <clocale>,

SetConsoleCP()SetConsoleOutputCP() từ <windows.h> và nhiều, rất nhiều những người khác.

Cuối cùng tôi đã bị làm phiền bởi chủ nghĩa shaman này và tôi muốn hỏi bạn: làm thế nào nó là chính xác để thiết lập mã hóa?

+0

Chính xác, bạn muốn thay đổi điều gì? Bạn có muốn thay đổi ngôn ngữ của chuỗi không? Ngôn ngữ hệ thống? Ngôn ngữ giao diện người dùng? Hoặc trang mã hoạt động? Đối với luồng, bảng điều khiển hoặc hệ thống? Có rất nhiều tùy chọn, nhiều hơn hàm ý của một hàm 'setlocale' duy nhất. Bạn phải giải thích hiệu ứng * mà bạn muốn xem trước khi chúng tôi có thể cho bạn biết chuyển đổi nào lật. –

+0

@CodyGray, tôi cần bất kỳ biểu tượng/chuỗi Unicode nào được nhập chính xác và được xuất ra. Mô tả đầy đủ này của _effect_? Tôi nghĩ rằng, nó có nghĩa là tôi cần phải thay đổi mã hóa của giao diện điều khiển trong đó chương trình được bắt đầu. –

+0

Nói chung, tôi muốn nói một chương trình không nên sửa đổi ngôn ngữ - nó sẽ hoạt động trong miền địa phương được cung cấp. Nếu không, nó sẽ đánh bại mục đích "quốc tế hóa". –

Trả lời

5

Tôi cần bất kỳ biểu tượng/chuỗi Unicode nào được nhập chính xác và được xuất ra.

Điều này chắc chắn có thể xảy ra, mặc dù làm cho bảng điều khiển nhắc lệnh Windows đúng cách nhận thức Unicode có một số phép thuật đặc biệt. Tôi nghiêm túc nghi ngờ rằng bất kỳ việc triển khai các chức năng thư viện chuẩn nào sẽ làm điều này, thật không may.

Bạn sẽ tìm thấy một số câu hỏi về nó trên Stack Overflow, nhưng this one is a good one. Về cơ bản, giao diện điều khiển sử dụng những gì được gọi là (phần nào sai) trang mã "OEM" theo mặc định. Bạn muốn thay đổi điều đó thành trang mã UTF-8, giá trị được xác định bởi CP_UTF8. Để thực hiện việc này, bạn cần phải gọi cả hai chức năng SetConsoleCP (để đặt đầu vào trang mã) và chức năng SetConsoleOutputCP (để đặt đầu ra trang mã). Mã sẽ trông giống như sau:

if (!SetConsoleCP(CP_UTF8)) 
{ 
    // An error occurred; handle it. Call GetLastError() for more information. 
    // ... 
} 
if (!SetConsoleOutputCP(CP_UTF8)) 
{ 
    // An error occurred; handle it. Call GetLastError() for more information. 
    // ... 
} 

Để tăng cường độ mạnh, bạn cũng có thể muốn đảm bảo rằng trang mã UTF-8 được hỗ trợ trước, trước khi thử đặt và sử dụng. Bạn sẽ làm điều đó bằng cách gọi hàm IsValidCodePage. Ví dụ:

if (IsValidCodePage(CP_UTF8)) 
{ 
    // We're all good, so set the console code page... 
} 

Bạn cũng sẽ phải thay đổi phông chữ từ mặc định ("Raster Fonts") để một cái gì đó có chứa các ký tự Unicode cần thiết glyphs-ví dụ, Lucida Console hoặc Consolas (reference).. Đó là tầm thường để làm bằng cách sử dụng chức năng SetCurrentConsoleFontEx.

Thật không may, chức năng này không tồn tại trong các phiên bản Windows trước Vista. Nếu bạn hoàn toàn cần hỗ trợ các hệ điều hành cũ hơn, điều duy nhất tôi biết phải làm là gọi hàm SetConsoleFont không có giấy tờ. Thông thường, tôi sẽ tư vấn cho mạnh mẽ chống lại việc sử dụng các chức năng không có giấy tờ, nhưng tôi nghĩ rằng vấn đề ở đây ít hơn vì bạn chỉ chỉ sử dụng trong các phiên bản cũ của hệ điều hành.Bạn biết những người đó sẽ không thay đổi. Trên các phiên bản mới hơn có sẵn, bạn gọi hàm được hỗ trợ. Mã mẫu chưa được kiểm tra:

bool IsWinVistaOrLater() 
{ 
    OSVERSIONINFOEX osvi; 
    osvi.dwOSVersionInfoSize = sizeof(osvi); 
    GetVersionEx(reinterpret_cast<LPOSVERSIONINFO>(&osvi)); 

    if (osvi.dwPlatformId == VER_PLATFORM_WIN32_NT) 
    { 
     return osvi.dwMajorVersion >= 6; 
    } 
    return false; 
} 

void SetConsoleToUnicodeFont() 
{ 
    HANDLE hConsole = GetStdHandle(STD_OUTPUT_HANDLE); 
    if (IsWinVistaOrLater()) 
    { 
     // Call the documented function. 
     typedef BOOL (WINAPI * pfSetCurrentConsoleFontEx)(HANDLE, BOOL, PCONSOLE_FONT_INFOEX); 
     HMODULE hMod = GetModuleHandle(TEXT("kernel32.dll")); 
     pfSetCurrentConsoleFontEx pfSCCFX = (pfSetCurrentConsoleFontEx)GetProcAddress(hMod, "SetCurrentConsoleFontEx"); 

     CONSOLE_FONT_INFOEX cfix; 
     cfix.cbSize  = sizeof(cfix); 
     cfix.nFont  = 12; 
     cfix.dwFontSize.X = 8; 
     cfix.dwFontSize.Y = 14; 
     cfix.FontFamily = FF_DONTCARE; 
     cfix.FontWeight = 400; // normal weight 
     lstrcpy(cfix.FaceName, TEXT("Lucida Console")); 

     pfSCCFX(hConsole, 
       FALSE, /* set font for current window size */ 
       &cfix); 
    } 
    else 
    { 
     // There is no supported function on these older versions, 
     // so we have to call the undocumented one. 
     typedef BOOL (WINAPI * pfSetConsoleFont)(HANDLE, DWORD); 
     HMODULE hMod = GetModuleHandle(TEXT("kernel32.dll")); 
     pfSetConsoleFont pfSCF = (pfSetConsoleFont)GetProcAddress(hMod, "SetConsoleFont"); 
     pfSCF(hConsole, 12); 
    } 
} 

Lưu ý rằng tôi đã thêm lỗi bắt buộc kiểm tra làm bài tập cho người đọc. Trọng tâm ở đây là về kỹ thuật và khả năng đọc; lộn xộn nó với xử lý lỗi sẽ chỉ gây nhầm lẫn vấn đề.

Tôi không biết làm cách nào để thực hiện điều này trên Linux. Tôi nghi ngờ đó là công việc ít hơn rất nhiều, vì mọi người nói với tôi rằng hệ điều hành sử dụng UTF-8 nội bộ. Dù bằng cách nào, bạn đang ở trên của riêng bạn cho điều đó; làm cho Windows purr đủ làm việc cho một câu trả lời!

0

Tôi chỉ cần xuất Văn bản Unicode vào bảng điều khiển và chỉ chức năng này WriteConsoleW(GetStdHandle(STD_OUTPUT_HANDLE), ...); đã giúp. Đối với đầu vào, tôi giả sử ReadConsoleW(GetStdHandle(STD_INPUT_HANDLE), ...); thực hiện thủ thuật.

PS: WriteOutput có giới hạn về kích thước chuỗi đầu ra. Vì vậy, bạn có thể muốn lặp lại nó theo khối nếu nó dài hơn.

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