2012-08-20 70 views
9

Tôi đang viết một chương trình cần để có thể làm việc với văn bản bằng tất cả các ngôn ngữ. Sự hiểu biết của tôi là UTF-8 sẽ thực hiện công việc, nhưng tôi đang gặp một số vấn đề với nó.UTF-8 Khả năng tương thích trong C++

Tôi có quyền nói rằng UTF-8 có thể được lưu trữ trong một đơn giản char trong C++ không? Nếu vậy, tại sao tôi nhận được cảnh báo sau khi tôi sử dụng chương trình với char, stringstringstream: warning C4566: character represented by universal-character-name '\uFFFD' cannot be represented in the current code page (1252). (Tôi không gặp lỗi đó khi tôi sử dụng wchar_t, wstringwstringstream.)

Ngoài ra, tôi biết rằng UTF có độ dài thay đổi. Khi tôi sử dụng các phương pháp chuỗi at hoặc substr thì tôi có nhận được câu trả lời sai không?

+0

Đối với UTF wchar_t được đề xuất lưu trữ.Bạn có thể lưu trữ UTF-8 trong char mà không có vấn đề nhưng kết quả sẽ là lạ. – perilbrain

+4

@Anonymous phụ thuộc vào nền tảng của bạn (và trên * mà * hương vị của UTF mà bạn quan tâm). Trên Windows, 'wchar_t' phù hợp với UTF-16. Trên Linux, nó phù hợp để sử dụng cho UTF-32. Đối với UTF-8, 'char' là một ứng cử viên khá hợp lý (trừ khi bạn có quyền truy cập vào các kiểu ký tự" mới "trong C++ 11) – jalf

+0

Chương trình này sẽ được chuyển qua các nền tảng. Loại ký tự nào có thể được sử dụng tốt nhất cho mục đích đó? – Qman

Trả lời

11

Để sử dụng các chuỗi ký tự UTF-8, bạn cần thêm tiền tố với u8, nếu không bạn sẽ nhận được bộ ký tự thực hiện (trong trường hợp của bạn, có vẻ như là Windows-1252): u8"\uFFFD" Biểu diễn UTF-8 của ký tự thay thế (U + FFFD). Nó có loại char const[4].

Vì UTF-8 có độ dài thay đổi, tất cả các loại lập chỉ mục sẽ lập chỉ mục trong các đơn vị mã, không phải là các điểm mã. Không thể thực hiện truy cập ngẫu nhiên trên các điểm mã trong một chuỗi UTF-8 vì nó có tính chất chiều dài biến đổi. Nếu bạn muốn truy cập ngẫu nhiên, bạn cần sử dụng mã hóa độ dài cố định, như UTF-32. Cho rằng bạn có thể sử dụng tiền tố U trên chuỗi.

+2

Tôi đã sử dụng tiền tố 'L' cho đến nay. Tôi đã thử thay thế nó bằng 'u8' nhưng tôi gặp lỗi 'C2065:' u8 ': số nhận dạng không khai báo'. – Qman

+1

@ user1563613 Có thể trình biên dịch của bạn chưa hỗ trợ 'u8'. Có phải Visual Studio không? Nếu vậy bạn nên sử dụng UTF-16, đó là những gì các API Windows sử dụng. –

+0

Nó là Visual studio 2010. Nếu tôi sử dụng UTF-16 tôi phải chỉ định endianess, đúng không? Nếu vậy, đó sẽ không phải là một vấn đề khi chuyển chương trình này sang các nền tảng khác? – Qman

1

Lý do bạn nhận được cảnh báo về \uFFFD là bạn đang cố gắng phù hợp với FF FD bên trong một byte, vì, như bạn đã lưu ý, UTF-8 hoạt động trên char s và có độ dài thay đổi.

Nếu bạn sử dụng at hoặc substr, bạn có thể sẽ nhận được câu trả lời sai vì các phương pháp này tính rằng một byte phải là một ký tự. Đây không phải là trường hợp với UTF-8. Đáng chú ý, với at, bạn có thể kết thúc bằng một byte đơn của chuỗi ký tự; với substr, bạn có thể ngắt chuỗi và kết thúc bằng chuỗi UTF-8 không hợp lệ (nó sẽ bắt đầu hoặc kết thúc bằng , \uFFFD, giống như bạn đang cố gắng sử dụng và ký tự bị hỏng sẽ bị mất).

Tôi khuyên bạn nên sử dụng wchar để lưu trữ chuỗi Unicode. Vì loại có ít nhất 16 bit, nhiều ký tự nhiều hơn có thể vừa với một "đơn vị" duy nhất.

+0

Phần tồi tệ nhất là nó sẽ không kết thúc với một nhân vật thay thế. Phá vỡ một chuỗi các byte UTF-8 ở sai vị trí với chất nền đơn giản dẫn đến một chuỗi không hợp lệ. Để nhận các ký tự thay thế, bạn cần phải xác thực và thay thế chúng theo cách thủ công. –

+0

@ R.MartinhoFernandes, thực sự. Tuy nhiên, tôi sẽ tin rằng vào thời điểm dữ liệu được trình bày cho người dùng, một số lớp của ngăn xếp sẽ thực hiện công việc. (Tuy nhiên, như bạn đã lưu ý, nó sẽ vẫn không được sửa trong chương trình C++.) – zneak

+0

Vậy làm thế nào tôi sẽ đi đúng về việc nhận được các chất nền hoặc lặp qua các ký tự? – Qman

9

Có, mã hóa UTF-8 có thể được sử dụng với char, string và stringstream. Một char sẽ giữ một đơn vị mã UTF-8, trong đó tối đa bốn có thể được yêu cầu để đại diện cho một điểm mã Unicode duy nhất.

Tuy nhiên, có một số vấn đề khi sử dụng UTF-8 đặc biệt với trình biên dịch của Microsoft. Việc triển khai C++ sử dụng 'bộ ký tự thực hiện' cho một số thứ, chẳng hạn như ký tự mã hóa và chuỗi ký tự chuỗi. VC++ luôn sử dụng mã hóa miền địa phương hệ thống làm bộ ký tự thực hiện và Windows không hỗ trợ UTF-8 làm mã hóa miền địa phương hệ thống, do đó UTF-8 không bao giờ được thực hiện bởi bộ ký tự thực thi.

Điều này có nghĩa là VC++ không bao giờ cố tình tạo ra ký tự UTF-8 và chuỗi ký tự chuỗi. Thay vào đó trình biên dịch phải được lừa.

Trình biên dịch sẽ chuyển đổi từ mã hóa mã nguồn đã biết thành mã hóa thực thi. Điều đó có nghĩa là nếu trình biên dịch sử dụng mã hóa miền địa phương cho cả mã hóa nguồn và mã thực thi thì không có chuyển đổi nào được thực hiện.Nếu bạn có thể nhận được dữ liệu UTF-8 vào mã nguồn nhưng có trình biên dịch nghĩ rằng nguồn sử dụng mã hóa miền địa phương, thì ký tự chữ cái và chuỗi ký tự sẽ sử dụng mã hóa UTF-8. VC++ sử dụng cái gọi là 'BOM' để phát hiện mã hóa nguồn và sử dụng mã hóa miền địa phương nếu không phát hiện BOM. Do đó bạn có thể nhận được các chuỗi ký tự chuỗi được mã hóa UTF-8 bằng cách lưu tất cả các tệp nguồn của bạn dưới dạng "UTF-8 không có chữ ký".

Hãy cẩn thận với phương pháp này. Trước tiên, bạn không thể sử dụng UCN với ký tự hẹp và chuỗi ký tự. Tên nhân vật phổ quát phải được chuyển đổi thành bộ ký tự thực thi, không phải là UTF-8. Bạn phải viết ký tự theo nghĩa đen để nó xuất hiện dưới dạng mã UTF-8 trong mã nguồn hoặc bạn có thể sử dụng dấu thoát hex nơi bạn viết mã UTF-8 theo cách thủ công. Thứ hai, để tạo ra các ký tự rộng và chuỗi ký tự chuỗi, trình biên dịch thực hiện một chuyển đổi tương tự từ mã hóa nguồn thành tập ký tự thực thi rộng (luôn là UTF-16 trong VC++). Vì chúng ta đang nói dối với trình biên dịch về mã hóa, nó sẽ thực hiện chuyển đổi này sang UTF-16 không chính xác. Vì vậy, trong nhân vật rộng và chuỗi ký tự bạn không thể sử dụng các ký tự không phải ascii theo nghĩa đen, và thay vào đó bạn phải sử dụng UCN hoặc thoát hex.


UTF-8 có độ dài thay đổi (như là UTF-16). Các chỉ mục được sử dụng với at()substr()đơn vị mã thay vì chỉ số điểm ký tự hoặc mã. Vì vậy, nếu bạn muốn có một đơn vị mã cụ thể thì bạn chỉ có thể lập chỉ mục vào chuỗi hoặc mảng hoặc bất kỳ thứ gì như bình thường. Nếu bạn cần một điểm mã cụ thể thì bạn cần một thư viện có thể hiểu việc soạn các đơn vị mã UTF-8 thành các điểm mã (chẳng hạn như Boost Unicode iterators library) hoặc bạn cần chuyển đổi dữ liệu UTF-8 thành UTF-32. Nếu bạn cần các ký tự được người dùng nhận thức thực tế thì bạn cần một thư viện để hiểu cách các điểm mã được tạo thành các ký tự. Tôi tưởng tượng ICU có chức năng như vậy, hoặc bạn có thể thực hiện các Default Grapheme Cluster Boundary Specification từ tiêu chuẩn Unicode.


Việc xem xét trên UTF-8 chỉ thực sự quan trọng đối với cách bạn ghi dữ liệu Unicode trong mã nguồn. Nó có ít mang về đầu vào và đầu ra của chương trình.

Nếu yêu cầu của bạn cho phép bạn chọn cách thực hiện đầu vào và đầu ra thì tôi vẫn khuyên bạn nên sử dụng UTF-8 cho đầu vào. Tùy thuộc vào những gì bạn cần làm với đầu vào, bạn có thể chuyển đổi nó sang mã hóa khác dễ dàng để bạn xử lý hoặc bạn có thể viết các quy trình xử lý của mình để làm việc trực tiếp trên UTF-8.

Nếu bạn muốn sản xuất bất kỳ thứ gì thông qua bảng điều khiển Windows, bạn sẽ muốn có một mô đun được xác định rõ ràng cho đầu ra có thể có các triển khai khác nhau, vì đầu ra quốc tế cho bàn điều khiển Windows sẽ yêu cầu thực hiện khác. trên Windows hoặc giao diện điều khiển và đầu ra tệp trên các nền tảng khác. (Trên các nền tảng khác, giao diện điều khiển chỉ là một tệp khác, nhưng giao diện điều khiển Windows cần xử lý đặc biệt.)

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