tl; dr
Sử dụng thư viện ICU . Nếu bạn không, thói quen chuyển đổi của bạn sẽ âm thầm phá vỡ các trường hợp mà bạn có thể không biết thậm chí còn tồn tại.
Đầu tiên bạn phải trả lời một câu hỏi: Mã hóa của bạn là std::string
gì? Có phải ISO-8859-1 không? Hoặc có lẽ ISO-8859-8? Hoặc Windows Codepage 1252? Có bất cứ điều gì bạn đang sử dụng để chuyển đổi chữ hoa sang chữ thường không? (Hay nó thất bại thảm hại cho các nhân vật hơn 0x7f
?)
Nếu bạn đang sử dụng UTF-8 (sự lựa chọn lành mạnh duy nhất trong số các mã hóa 8 bit) với std::string
dạng container, bạn đã tự lừa dối mình khi tin rằng bạn vẫn đang kiểm soát mọi thứ, bởi vì bạn đang lưu trữ một chuỗi ký tự đa nhân trong một container đó là không nhận thức được khái niệm đa bào. Ngay cả một cái gì đó đơn giản như .substr()
là một khung thời gian tích tắc. (Vì việc tách chuỗi đa chuỗi sẽ dẫn đến chuỗi (phụ) không hợp lệ.)
Và ngay khi bạn thử một cái gì đó như std::toupper( 'ß' )
, trong bất kỳ mã hóa nào , bạn sẽ gặp rắc rối sâu sắc. (Bởi vì đơn giản là không thể thực hiện "quyền" này với thư viện chuẩn, chỉ có thể cung cấp một ký tự kết quả, không "SS"
cần thiết ở đây.) [1] Một ví dụ khác sẽ std::tolower( 'I' )
mang lại kết quả khác nhau tùy theo miền địa phương . Ở Đức, 'i'
sẽ đúng; ở Thổ Nhĩ Kỳ, 'ı'
(LATIN SMALL LETTER DOTLESS I) là kết quả mong đợi (một lần nữa, nhiều hơn một byte trong mã hóa UTF-8). Một ví dụ khác là Sigma Hy Lạp , chữ hoa '∑'
, chữ thường 'σ'
... ngoại trừ ở cuối từ, nó ở đâu 'ς'
.
Vì vậy, bất kỳ chuyển đổi trường hợp nào hoạt động trên một ký tự tại một thời điểm, hoặc tệ hơn, một byte tại một thời điểm, đều bị phá vỡ bởi thiết kế.
Sau đó là điểm mà các thư viện chuẩn, cho những gì nó là có khả năng làm, là tùy thuộc vào miền địa phương được hỗ trợ trên máy phần mềm của bạn đang chạy trên ... và bạn sẽ làm gì nếu nó không phải là?
Vì vậy, những gì bạn đang thực sự tìm kiếm là một lớp chuỗi có khả năng xử lý tất cả điều này một cách chính xác, và đó không phải là bất kỳ std::basic_string<>
biến thể nào .
(C ++ 11 lưu ý: std::u16string
và std::u32string
là tốt hơn ., Nhưng vẫn không hoàn thiện C ++ 20 mang std::u8string
, nhưng tất cả những việc phải làm là xác định mã hóa Trong nhiều khía cạnh khác mà họ vẫn không biết gì về cơ khí Unicode, như bình thường, đối chiếu, ... .)
Mặc dù Boost có vẻ đẹp, API thông minh, Boost.Locale về cơ bản là một trình bao bọc xung quanh ICU . Nếu Boost được biên dịch với hỗ trợ ICU ... nếu không, Boost.Locale bị giới hạn ở hỗ trợ ngôn ngữ được biên dịch cho thư viện chuẩn.
Và tin tôi đi, đôi khi việc Boost được biên dịch với ICU có thể là một nỗi đau thực sự. (Không có nhị phân được biên dịch sẵn cho Windows, vì vậy bạn phải cung cấp chúng cùng với ứng dụng của mình và điều đó sẽ mở ra một hộp giun hoàn toàn mới ...)
Vì vậy, cá nhân tôi khuyên bạn nên nhận hỗ trợ Unicode đầy đủ trực tiếp từ miệng ngựa và sử dụng thư viện ICU trực tiếp:
#include <unicode/unistr.h>
#include <unicode/ustream.h>
#include <unicode/locid.h>
#include <iostream>
int main()
{
/* "Odysseus" */
char const * someString = u8"ΟΔΥΣΣΕΥΣ";
icu::UnicodeString someUString( someString, "UTF-8" );
// Setting the locale explicitly here for completeness.
// Usually you would use the user-specified system locale,
// which *does* make a difference (see ı vs. i above).
std::cout << someUString.toLower( "el_GR" ) << "\n";
std::cout << someUString.toUpper( "el_GR" ) << "\n";
return 0;
}
Biên dịch (với G ++ trong ví dụ này):
g++ -Wall example.cpp -licuuc -licuio
Điều này mang lại:
ὀδυσσεύς
Lưu ý rằng chuyển đổi Σ <-> in ở giữa từ và chuyển đổi Σ <-> ở cuối từ. Không có <algorithm>
giải pháp dựa trên có thể cung cấp cho bạn điều đó.
. ). Ví dụ đẹp của tôi, đã bị lỗi thời bởi quyết định của ủy ban ...