2012-02-10 22 views
6

Tôi muốn đếm các ký tự (trong các bộ ký tự khác nhau) trong một tệp và tôi đang sử dụng hàm 'mbtowc' để phát hiện các ký tự. Tôi không thể tìm ra lý do tại sao các giá trị ký tự và kết quả khác nhau. Dưới đây là ví dụ của tôi:Tại sao mbtowc đếm ký tự được đặt như mong đợi?

char buf[BUFFER_SIZE + MB_LEN_MAX]; 

int fd = open ("chinese_test", O_RDONLY); 

unsigned int bytes, chars; 

int bytes_read; 

bytes = chars = 0; 

while((bytes_read = read(fd, buf, BUFFER_SIZE)) > 0) { 
    wchar_t wc_buf[BUFFER_SIZE], *wcp; 
    char *p; 
    int n = 0; 

    bytes += bytes_read; 

    p = buf; 
    wcp = wc_buf; 

    while((n = mbtowc(wcp, p, MB_LEN_MAX)) > 0) { 
     p += n; 
     wcp++; 

     chars++; 
    } 

} 

printf("chars: %d\tbytes: %d\n", chars, bytes); 

tôi thử nghiệm chức năng với một văn bản với một số nhân vật GB2312, nhưng charsbyte là những giá trị quá khác nhau.

Trả về thử nghiệm của tôi -> ký tự: 4638 | byte: 17473 nhưng lệnh 'wc' linux trả về: ký tự: 16770 | byte: 17473

Tại sao sự khác biệt này? Tôi đã làm gì sai?


Bây giờ tôi đã có mã này nhưng vẫn có sự khác biệt so với kết quả.

char buf[BUFFER_SIZE * MB_LEN_MAX]; 

int fd = open ("test_chinese", O_RDONLY), filled = 0; 

unsigned int bytes, chars; 

int bytes_read; 

bytes = chars = 0; 

while((bytes_read = read(fd, buf, BUFFER_SIZE)) > 0) { 
    wchar_t wc_buf[BUFFER_SIZE], *wcp; 
    char *p; 
    int n = 0; 

    bytes += bytes_read; 

    p = buf; 
    wcp = wc_buf; 



    while(bytes_read > 0) { 
     n = mbtowc(NULL, p, MB_LEN_MAX); 

     if (n <= 0) { 
      p++; 
      bytes_read--; 
      continue; 
     } 
     p += n; 

     bytes_read -= n; 

     chars++; 
    } 

} 

printf("\n\nchars: %d\tbytes: %d\n", chars, bytes); 
+2

Tùy thuộc vào bạn 'BUFFER_SIZE' và kích thước của tập tin bạn đọc, bạn có thể nhận được chuỗi multibyte không đầy đủ . Thêm một dấu kiểm sau vòng lặp 'while' để xem liệu' n' có âm hay không. –

+0

@JoachimPileborg, tôi biết vấn đề này. Có thể là với một tập tin của 17473 Byte và với BUFFER_SIZE = 1024 là có rất nhiều lỗi? – Figus

+0

Có lẽ không, nhưng bạn nên kiểm tra điều này anyway. Thủ phạm có khả năng nhất là thủ phạm được nêu ra trong câu trả lời của Thụy Sĩ. –

Trả lời

6

Vấn đề là sự kết hợp của BUFFER_SIZE của bạn, kích thước tập tin của chinese_test và sự liên kết byte của wchar_t. Để làm bằng chứng, hãy thử tăng mạnh BUFFER_SIZE - bạn sẽ bắt đầu nhận được câu trả lời mình muốn.

Điều đang xảy ra là chương trình của bạn hoạt động cho khối văn bản đầu tiên nhận được. Nhưng hãy nghĩ về những gì xảy ra trong mã của bạn nếu một nhân vật được phân chia giữa các khối đầu tiên và thứ hai như sau:

| First Block     | Second Block  | 
    | [wchar_t] [wchar_t] ... [wchar_t] [wchar_t] ... | 
    | [1,2,3,4] [1,2,3,4] ... [1,2,3,4] [1,2,3,4] ... | 

Mã của bạn sẽ bắt đầu khối thứ hai trên byte thứ 3 trong ký tự đầu tiên, và điều đó sẽ không được được công nhận là một ký tự hợp lệ. Vì mbtowc sẽ trả về -1 khi không tìm thấy ký tự hợp lệ, vòng lặp của bạn sẽ kết thúc ngay lập tức và sẽ tính không có ký tự cho toàn bộ khối đó. Điều tương tự cũng áp dụng cho các khối sau.

EDIT:
Một vấn đề khác mà tôi nhận thấy là bạn cần đặt ngôn ngữ để mbtowc hoạt động chính xác. Tham gia tất cả các vấn đề này vào tài khoản, tôi đã viết những điều sau đây mà trả về số ký tự giống như wc cho tôi:

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

int BUFFER_SIZE = 1024; 
const char *DEFAULT_F_IN = "chinese_test"; 

struct counts { 
    int bytes; 
    int chars; 
}; 

int count_block(struct counts *c, char *buf, int buf_size) 
{ 
    int offset = 0; 
    while (offset < buf_size) { 
     int n = mbtowc(NULL, buf + offset, MB_CUR_MAX); 
     if (n <= 0) { 
      break; 
     } 

     offset += n; 
     c->bytes += n; 
     c->chars++; 
    } 

    return buf_size - offset; 
} 

void get_counts(struct counts *c, FILE *fd) 
{ 
    char buf[BUFFER_SIZE]; 
    c->bytes = 0; 
    c->chars = 0; 

    int bytes_read; 
    while((bytes_read = fread(buf, sizeof(*buf), BUFFER_SIZE, fd)) > 0) { 
     int remaining = count_block(c, buf, bytes_read); 
     if (remaining == 0) { 
      continue; 
     } else if (remaining < MB_CUR_MAX) { 
      fseek(fd, -remaining, SEEK_CUR); 
     } else { 
      perror("Error"); 
      exit(1); 
     } 
    } 
} 

int main(int argc, char *argv[]) { 
    FILE *fd; 
    if (argc > 1) { 
     fd = fopen(argv[1], "rb"); 
    } else { 
     fd = fopen(DEFAULT_F_IN, "rb"); 
    } 

    setlocale(LC_ALL, ""); 
    struct counts c; 
    get_counts(&c, fd); 
    printf("chars: %d\tbytes: %d\n", c.chars, c.bytes); 

    return 0; 
} 
+3

'mbtowc()' trả về -1 nếu có một ký tự multibyte không hoàn chỉnh. – caf

+0

@caf Rất tiếc. Cảm ơn cho bắt. Tôi đã sửa nó ngay bây giờ. – Swiss

+0

@Swiss Với điều này trong khi tôi đã _chars: 16634_; nhưng mã của bạn nâng cao một lỗi segmentation 'while (bytes_read> 0) { \t \t \t n = mbtowc (NULL, p, MB_LEN_MAX); \t \t \t if (n <= 0) { \t \t \t \t p ++; \t \t \t \t bytes_read--; \t \t \t \t tiếp tục; \t \t \t} \t \t \t p + = n; \t \t \t \t \t \t bytes_read - = n; \t \t \t \t \t \t ký tự ++; \t \t} ' – Figus

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