2015-11-16 24 views
7

Tôi cố gắng phân tích văn bản và tìm một số ký tự trong đó. Tôi sử dụng mã dưới đây. Nó hoạt động với các ký tự bình thường như abcdef nhưng nó không hoạt động với öçşğüı. GCC đưa ra cảnh báo biên dịch. Tôi nên làm gì để làm việc với öçşğüı?Cách so sánh các ký tự nhiều byte trong C

Code:

#include <stdio.h> 
#include <ctype.h> 
#include <string.h> 

int main() 
{ 
    char * text = "öçşğü"; 
    int i=0; 

    text = strdup(text); 

    while (text[i]) 
    {  
     if(text[i] == 'ö') 
     { 
      printf("ö \n"); 
     } 

     i++; 
    } 

    return 0; 
} 

Cảnh báo:

warning: multi-character character constant [-Wmultichar] 
warning: comparison is always false due to limited range of data type [-Wtype-limits] 

Có 10 địa chỉ khi tôi in địa chỉ của char trong vòng lặp while

printf("%d : %p \n", i, text[i]); 

đầu ra:

0 : 0xffffffc3 
1 : 0xffffffb6 
2 : 0xffffffc3 
3 : 0xffffffa7 
4 : 0xffffffc5 
5 : 0xffffff9f 
6 : 0xffffffc4 
7 : 0xffffff9f 
8 : 0xffffffc3 
9 : 0xffffffbc 

strlen là 10.

Nhưng nếu tôi sử dụng abcde:

0 : 0x61 
1 : 0x62 
2 : 0x63 
3 : 0x64 
4 : 0x65 

strlen là 5.


Nếu tôi sử dụng wchar_t cho đầu ra văn bản được

0 : 0xa7c3b6c3 
1 : 0x9fc49fc5 
2 : 0xbcc3 

strlen là 10, wcslen là 3.

+1

Tôi nghĩ đó là bởi vì umlauts được coi là một nhân vật khác. Bạn có thể phải kiểm tra chuỗi thực tế thay vì ký tự hoặc lấy 'oe' thay vì' ö' làm đầu vào. – Arc676

+0

llvm đưa ra một lỗi rõ ràng: ký tự quá lớn để kèm theo ký tự kiểu chữ nếu (văn bản [i] == 'ö') –

+0

'strncmp()' có thể hữu ích. – MikeCAT

Trả lời

-1

Cách tốt nhất để xử lý các ký tự rộng là như, tốt, ký tự rộng.

wchar_t myWord[] = L"Something"; 

này sẽ làm điều đó:

#include <stdio.h> 
#include <ctype.h> 
#include <string.h> 

int main() 
{ 
    wchar_t * text = L"öçşğü"; 
    int i = 0; 

    while (text[i]) 
    { 
     if (text[i] == L'ö') 
     { 
      wprintf(L"ö \n"); 
     } 

     i++; 
    } 

    return 0; 
} 

Nếu bạn đang ở trong Visual Studio, như tôi, nhớ lại rằng cửa sổ giao diện điều khiển không xử lý Unicode tốt. Bạn có thể chuyển hướng tệp đó đến tệp và kiểm tra tệp và xem ö.

+1

Theo tôi, sử dụng 'wchar_t' là cách * tồi tệ nhất *, vì trong Visual Studio chúng là 16-bit, và yêu cầu UTF- Mã hóa 16. Vì vậy, mã bỏ qua mã hóa (như 'i ++') sẽ có các lỗi của nó được che dấu - bạn sẽ thấy các lỗi trong có thể 0,1% chuỗi, thay vì có thể 10% chuỗi. – anatolyg

1

Để xem qua từng ký tự trong chuỗi, bạn có thể sử dụng mblen. Bạn cũng cần phải đặt ngôn ngữ chính xác (mã hóa được biểu thị bằng chuỗi nhiều byte), sao cho mblen có thể phân tích cú pháp chuỗi đa byte một cách chính xác.

#include <stdio.h> 
#include <string.h> 
#include <stdlib.h> 
#include <locale.h> 

int main() 
{ 
    char * text = "öçşğü"; 
    int i=0, char_len; 

    setlocale(LC_CTYPE, "en_US.utf8"); 

    while ((char_len = mblen(&text[i], MB_CUR_MAX)) > 0) 
    { 
     /* &text[i] contains multibyte character of length char_len */ 
     if(memcmp(&text[i], "ö", char_len) == 0) 
     { 
      printf("ö \n"); 
     } 

     i += char_len; 
    } 

    return 0; 
} 

Có 2 loại biểu diễn chuỗi, sử dụng byte nhiều byte (byte 8 bit) hoặc byte rộng (kích thước phụ thuộc vào nền tảng). Biểu diễn nhiều byte có lợi thế nó có thể được biểu diễn bằng cách sử dụng char * (chuỗi c thông thường như trong mã của bạn), nhưng có bất lợi là nhiều byte biểu thị một ký tự. Chuỗi rộng được thể hiện bằng cách sử dụng wchar_t *. wchar_t có lợi thế là một wchar_t là một ký tự (Tuy nhiên như @anatolyg chỉ ra, giả định này vẫn có thể đi sai trong các nền tảng mà wchar_t không thể đại diện cho tất cả các ký tự có thể).

Bạn đã xem mã nguồn của mình bằng trình chỉnh sửa hex chưa?Chuỗi "öçşğü" thực sự được biểu diễn bằng chuỗi nhiều byte c3 b6 c3 a7 c5 9f c4 9f c3 bc trong bộ nhớ (mã hóa UTF-8), tất nhiên là không có chấm dứt. Bạn thấy 5 ký tự chỉ vì chuỗi được hiển thị chính xác bởi trình xem/trình duyệt nhận biết UTF-8 của bạn. Nó là đơn giản để nhận ra rằng strlen(text) trả về 10 cho điều này, trong khi mã trên chỉ lặp lại 5 lần.

Nếu bạn sử dụng chuỗi byte rộng, nó có thể được thực hiện như được giải thích bởi @WillBriggs.

+0

Mã của bạn hỏi 'Tại sao điều này là cần thiết?', Có vẻ lạ. Bạn đã sao chép nó từ đâu đó cùng với nhận xét gây hiểu lầm này, hay bạn tự hỏi tại sao bạn cần 'strdup'? Tôi nghĩ rằng hoàn toàn không có lý do để sử dụng 'strdup' ở đây (BTW' strdup' phải được ghép nối với 'miễn phí', mà là thiếu trong mã của bạn). – anatolyg

+0

Tôi chỉ muốn hỏi người đăng ban đầu, tại sao anh ấy sử dụng strdup. Tất nhiên, nó không cần thiết, chỉ để phân tích cú pháp. Tôi sẽ chỉnh sửa câu trả lời của mình. Cảm ơn bạn đã chỉ ra. – user1969104

+0

@ user1969104 cảm ơn câu trả lời. Tôi đã thử mã của bạn trong linux nhưng không bao giờ in 'ö' để bàn điều khiển. Bạn có thể xem trên http://ideone.com/2arnRE. Nhưng nó hoạt động với 'abcde' – user4757345

0

Không có tiêu chuẩn nào xung quanh việc nhúng các ký tự không phải ASCII trực tiếp vào tệp nguồn của bạn.

Thay vào đó, các tiêu chuẩn C11 quy định cụ thể mà bạn có thể sử dụng Unicode code points:

wchar_t text[] = L"\u00f6\u00e7\u015f\u0131\u011f"; 

// Print whole string 
wprintf(L"%s\n", text); 

// Test individual characters 
for (size_t i = 0; text[i]; ++i) 
{ 
    if (text[i] == u'\u00f6') 
     // whatever... 
} 

Nếu bạn đang dùng Windows thì bạn phải đối mặt với một vấn đề bổ sung mà Windows giao diện điều khiển không thể in các ký tự Unicode bởi mặc định. Bạn cần làm như sau:

  • Thay đổi bảng điều khiển để sử dụng phông chữ đơn cách TrueType bao gồm glyphs cho các ký tự bạn đang cố in. (Tôi đã sử dụng "DejaVu Sans Mono" cho ví dụ này)
  • Trong mã nguồn, hãy gọi hàm _setmode(1, _O_WTEXT);, sẽ cần #include <fcntl.h>.

Để khôi phục văn bản bình thường sau đó bạn có thể _setmode(1, _O_TEXT);.

Tất nhiên, nếu bạn đang xuất ra tệp hoặc chức năng API Win32 thì bạn không cần thực hiện các bước đó.

+0

cảm ơn câu trả lời @ M.M. Nhưng tôi nhận được lỗi này 'lỗi:' u 'không khai báo (sử dụng đầu tiên trong hàm này) '. Nếu tôi thay đổi 'u' bằng' L' code. – user4757345

+0

OK. Trình biên dịch của bạn có thể không hỗ trợ C11 (hoặc bạn không gọi nó trong chế độ C11 nếu nó có) –

0

Xem wiki tại đây: https://en.wikipedia.org/wiki/UTF-8 Cụ thể, có một bảng có mẫu bit.

Dưới đây là một cách khác để quét/chuyển đổi một chuỗi utf-8 vào một codepoint [không chính xác, chỉ là ví dụ - tham khảo wiki]:

// utf8scan -- convert utf8 to codepoints (example) 

char inpbuf[1000]; 
char uni[8]; 

typedef union { 
    char utf8[4]; 
    unsigned int code; 
} codepoint_t; 

codepoint_t outbuf[1000]; 

// unidecode -- decode utf8 char into codepoint 
// RETURNS: updated rhs pointer 
char * 
unidecode(codepoint_t *lhs,char *rhs) 
{ 
    int idx; 
    int chr; 

    idx = 0; 
    lhs->utf8[idx++] = *rhs++; 

    for (; ; ++rhs, ++idx) { 
     chr = *rhs; 

     // end of string 
     if (chr == 0) 
      break; 

     // start of new ascii char 
     if ((chr & 0x80) == 0) 
      break; 

     // start of new unicode char 
     if (chr & 0x40) 
      break; 

     lhs->utf8[idx] = chr; 
    } 

    return rhs; 
} 

// main -- main program 
int 
main(void) 
{ 
    char *rhs; 
    codepoint_t *lhs; 

    rhs = inpbuf; 
    lhs = outbuf; 

    for (; *rhs != 0; ++lhs) { 
     lhs->code = 0; 

     // ascii char 
     if ((*rhs & 0x80) == 0) 
      lhs->utf8[0] = *rhs++; 

     // get/skip unicode char 
     else 
      rhs = unidecode(lhs,rhs); 
    } 

    // add EOS 
    lhs->code = 0; 

    return 0; 
} 
Các vấn đề liên quan