2013-04-25 20 views
7

Tôi không biết làm thế nào để giải quyết rằng:Làm thế nào để làm việc với UTF-8 trong C++, chuyển đổi từ bảng mã khác sang UTF-8

Hãy tưởng tượng, chúng ta có 4 trang web:

  • A: UTF-8
  • B: ISO-8859-1
  • C: ASCII
  • D: UTF-16

My Chương trình được viết bằng C++ thực hiện như sau: Nó tải xuống một trang web và phân tích nó. Nhưng nó phải hiểu nội dung. Vấn đề của tôi không phải là phân tích cú pháp được thực hiện với các ký tự ASCII như ">" hoặc "<".

Vấn đề là chương trình sẽ tìm tất cả các từ trong văn bản của trang web. Một từ là bất kỳ sự kết hợp nào của các ký tự chữ và số. Sau đó, tôi gửi những từ này đến một máy chủ. Cơ sở dữ liệu và giao diện người dùng web đang sử dụng UTF-8. Vì vậy, câu hỏi của tôi là:

  • Làm thế nào tôi có thể chuyển đổi "bất kỳ" (hoặc sử dụng nhiều nhất) mã hóa ký tự UTF-8?
  • Làm cách nào tôi có thể làm việc với UTF-8-strings trong C++? Tôi nghĩ rằng wchar_t không hoạt động vì nó dài 2 byte. Mã-điểm trong UTF-8 dài tối đa 4 byte ...
  • Có các chức năng như isspace(), isalnum(), strlen(), tolower() cho các chuỗi UTF-8 này không?

Xin lưu ý: Tôi không thực hiện bất kỳ đầu ra nào (như std::cout) bằng C++. Chỉ lọc ra các từ và gửi chúng đến máy chủ.

Tôi biết về UTF8-CPP nhưng không có chức năng is*(). Và khi tôi đọc, nó không chuyển đổi từ mã hóa ký tự khác sang UTF-8. Chỉ từ UTF- * đến UTF-8.

Edit: Tôi quên nói, rằng chương trình đã được cầm tay: Windows, Linux, ...

+2

Thư viện đa nền tảng tốt để xử lý Unicode (thuộc tính codepoint, chuyển đổi ký tự, v.v.) là [ICU của IBM] (http://site.icu-project.org/) mặc dù nó có thể quá mức cần thiết cho nhu cầu của bạn. – syam

+2

* Tôi nghĩ wchar_t không hoạt động vì nó dài 2 byte * => nó tệ hơn 'wchar_t' là trình biên dịch/đích cụ thể, với MSVC nó sẽ dài 2 byte, nhưng với gcc và clang dài 4 byte. –

+0

@syam rất nhiều dự án và sản phẩm chỉ sử dụng ICU cho các chức năng chuyển đổi, vì vậy tôi sẽ không nghĩ nó là quá mức cần thiết. Bạn chỉ có thể sử dụng thư viện chung hoặc thậm chí là liên kết tĩnh. –

Trả lời

9

Làm thế nào tôi có thể chuyển đổi "bất kỳ" (hoặc sử dụng nhiều nhất) mã hóa ký tự UTF-8?

ICU (Thành phần quốc tế cho Unicode) là giải pháp tại đây. Nó thường được coi là người cuối cùng nói trong hỗ trợ Unicode. Ngay cả Boost.Locale và Boost.Regex sử dụng nó khi nói đến Unicode. Xem bình luận của tôi về câu trả lời của Dory Zidon về lý do tại sao tôi khuyên bạn nên sử dụng trực tiếp ICU, thay vì hàm bao (như Boost).

Bạn tạo một chuyển đổi cho một mã hóa cho ...

#include <ucnv.h> 

UConverter * converter; 
UErrorCode err = U_ZERO_ERROR; 
converter = ucnv_open("8859-1", &err); 
if (U_SUCCESS(error)) 
{ 
    // ... 
    ucnv_close(converter); 
} 

... và sau đó sử dụng lớp UnicodeString như appripriate.

Tôi nghĩ wchar_t không hoạt động vì nó dài 2 byte.

Kích thước của wchar_t được xác định thực hiện. AFAICR, Windows là 2 byte (UCS-2/UTF-16, tùy thuộc vào phiên bản Windows), Linux là 4 byte (UTF-32). Trong bất kỳ trường hợp nào, vì tiêu chuẩn không xác định ngữ nghĩa Unicode cho wchar_t, sử dụng nó là phỏng đoán không phải di động. Đừng đoán, sử dụng ICU.

Có các hàm như isspace(), isalnum(), strlen(), tolower() cho các chuỗi UTF-8 này không?

Không có mã UTF-8 của chúng, tuy nhiên, bạn không sử dụng nội bộ trong mã UTF-8. UTF-8 là tốt cho đại diện bên ngoài, nhưng nội bộ UTF-16 hoặc UTF-32 là sự lựa chọn tốt hơn. Các chức năng nêu trên tồn tại đối với các điểm mã Unicode (tức là, UChar32); ref. uchar.h.

Xin lưu ý: Tôi không thực hiện bất kỳ đầu ra nào (như std :: cout) trong C++. Chỉ lọc ra các từ và gửi chúng đến máy chủ.

Kiểm tra BreakIterator.

Edit: Tôi quên nói, rằng chương trình đã được cầm tay: Windows, Linux, ...

Trong trường hợp tôi đã không nói nó đã có, làm sử dụng ICU, và tiết kiệm cho mình rất nhiều rắc rối. Ngay cả khi nó có vẻ hơi nặng ở cái nhìn đầu tiên, nó thực hiện tốt nhất, cực kỳ di động (sử dụng nó trên Windows, Linux và AIX) và bạn sẽ sử dụng lại một lần nữa và một lần nữa trong các dự án sắp tới, vì vậy thời gian đầu tư vào việc học API của nó không bị lãng phí.

+1

Chỉ cần lưu ý: Nếu bạn sử dụng UTF-8 hoặc UTF-16 trong nội bộ, bạn không thể đại diện cho UTF-8 không hợp lệ, và thậm chí UTF-32 không giúp bạn tự do kết hợp chuỗi và các vấn đề khác. [Bản tuyên ngôn UTF-8 mọi nơi] (utf8everywhere.org) – Deduplicator

0

UTF-8 là một mã hóa sử dụng nhiều byte cho không phải ASCII (7 bit mã) sử dụng bit thứ 8. Như vậy bạn sẽ không tìm thấy '\', '/' bên trong một chuỗi nhiều byte. Và isdigit hoạt động (mặc dù không phải là chữ Ả Rập và các chữ số khác).

Đây là bộ siêu ký tự ASCII và có thể chứa tất cả các ký tự Unicode, vì vậy hãy chắc chắn sử dụng với char và chuỗi.

Kiểm tra tiêu đề HTTP (phân biệt chữ hoa chữ thường); chúng nằm trong ISO-8859-1 và đứng trước một dòng trống và sau đó là nội dung HTML.

Content-Type: text/html; charset=UTF-8 

Nếu không có mặt, cũng có thể có

<meta http-equiv="Content-Type" content="text/html; charset=UTF-8"> 
<meta charset="UTF-8">  <!-- HTML5 --> 

ISO-8859-1 là Latin 1, và bạn có thể làm tốt hơn để chuyển đổi từ Windows-1252,-1 Latin mở rộng Windows sử dụng 0x80 - 0xBF đối với một số ký tự đặc biệt như dấu phẩy và như vậy. Ngay cả các trình duyệt trên MacOS cũng sẽ hiểu những điều này mặc dù ISO-8859-1 đã được chỉ định.

Thư viện chuyển đổi: đã đọc được đề cập bởi @syam.

Chuyển đổi

Đừng xem xét UTF-16. Người ta có thể đọc các tiêu đề và bắt đầu cho đến khi một câu lệnh meta cho bộ ký tự là ký tự một byte.

Chuyển đổi từ mã hóa byte đơn thành UTF-8 có thể xảy ra thông qua bảng. Ví dụ được tạo bằng Java: một số const char* table[] được lập chỉ mục bởi char.

table[157] = "\xEF\xBF\xBD"; 


public static void main(String[] args) { 
    final String SOURCE_ENCODING = "windows-1252"; 
    byte[] sourceBytes = new byte[1]; 
    System.out.println(" const char* table[] = {"); 
    for (int c = 0; c < 256; ++c) { 
     String comment = ""; 
     System.out.printf("  /* %3d */ \"", c); 
     if (32 <= c && c < 127) { 
      // Pure ASCII 
      if (c == '\"' || c == '\\') 
       System.out.print("\\"); 
      System.out.print((char)c); 
     } else { 
      if (c == 0) { 
       comment = " // Unusable"; 
      } 
      sourceBytes[0] = (byte)c; 
      try { 
       byte[] targetBytes = new String(sourceBytes, SOURCE_ENCODING).getBytes("UTF-8"); 
       for (int j = 0; j < targetBytes.length; ++j) { 
        int b = targetBytes[j] & 0xFF; 
        System.out.printf("\\x%02X", b); 
       } 
      } catch (UnsupportedEncodingException ex) { 
       comment = " // " + ex.getMessage().replaceAll("\\s+", " "); // No newlines. 
      } 
     } 
     System.out.print("\""); 
     if (c < 255) { 
      System.out.print(","); 
     } 
     System.out.println(); 
    } 
    System.out.println(" };"); 
} 
+0

Như tôi đã nói: Quá trình phân tích cú pháp hoặc tìm ra mã hóa nào được sử dụng không phải là vấn đề. Vấn đề là chuyển đổi từ ví dụ. latin1 đến UTF-8. – Christoph

2

Không chắc chắn điều này sẽ cung cấp cho bạn mọi thứ bạn đang tìm kiếm nhưng nó có thể giúp ích một chút. Bạn đã thử xem xét:

1) Thư viện Boost.Locale? Boost.Locale được phát hành vào Boost 1,48 (15 tháng 11 2011) làm cho nó dễ dàng hơn để chuyển đổi từ và UTF8/16

Dưới đây là một số ví dụ thuận tiện từ các tài liệu:

string utf8_string = to_utf<char>(latin1_string,"Latin1"); 
wstring wide_string = to_utf<wchar_t>(latin1_string,"Latin1"); 
string latin1_string = from_utf(wide_string,"Latin1"); 
string utf8_string2 = utf_to_utf<char>(wide_string); 

2) Hoặc ít chuyển đổi là một phần của C++ 11?

#include <codecvt> 
#include <locale> 
#include <string> 
#include <cassert> 

int main() { 
    std::wstring_convert<std::codecvt_utf8<char32_t>, char32_t> convert; 
    std::string utf8 = convert.to_bytes(0x5e9); 
    assert(utf8.length() == 2); 
    assert(utf8[0] == '\xD7'); 
    assert(utf8[1] == '\xA9'); 
} 
+0

Giải pháp 1 với âm thanh Boost thực sự tốt! Tôi sẽ kiểm tra nó. Cảm ơn :) – Christoph

+2

@Christoph: Bạn có thể muốn lưu ý rằng khả năng Unicode của Boost.Locale được thực hiện bởi Boost.Locale về cơ bản là một trình bao bọc cho ICU ... và hãy để tôi nói cho bạn biết, hãy tăng cường liên kết tới ICU trên Windows là không tầm thường, có xu hướng phá vỡ giữa các bản phát hành và đã khiến tôi mất vài tuần trong vài năm qua. – DevSolar

0

Tôi có thể làm việc với UTF-8-strings bằng C++ bằng cách nào? Tôi nghĩ rằng wchar_t không hoạt động vì nó dài 2 byte. Code-Points tại UTF-8 là lên đến dài 4 byte ...

này rất dễ dàng, có một dự án có tên tiny-utf8, mà là một thả thay thế cho std::string/std::wstring.

Sau đó, người dùng có thể hoạt động thanh lịch trên điểm mã, trong khi biểu diễn của họ luôn được mã hóa theo số char s.


Làm thế nào tôi có thể chuyển đổi "bất kỳ" (hoặc sử dụng nhiều nhất) nhân vật mã hóa để UTF-8?

Bạn có thể muốn xem std::codecvt_utf8simlilar templates từ <codecvt> (C++ 11).