GBKとUTF-8の荒い判断


最近、いくつかのurlにencodeが落ちていない漢字が含まれており、このようなurlにはutf-8符号化、gbk符号化がある.最終的にこれらのurlが記録されると、必然的にurlが文字化けしてしまう.ある人は、中国語は博大で奥深いと言っていますが、まさにこれらの博大で奥深いものが時々私たちを悩ませています.このurl符号化の場合、最も専門的な手法はurlに符号化を説明するためのパラメータがあるべきだと思います.今、このurlが何の符号化なのか教えてくれない以上、私もできるだけ判断して、符号化を変換しなければなりません.
urlを識別する符号化には,その中の非ascii文字の数が少なすぎて誤審しやすいという難点がある.たとえば、私が出会ったurlの非ascii文字は、一般的に検索キーワードです.しかし、簡単な面ではurlの非ascii文字は一般的に漢字で、特殊文字、西洋文字などはほとんどありません.そのため、漢字の検出にターゲットをロックすることは、基本的に需要を満たすのに十分であり、少なくともこのような文字化けの状況を大幅に低減することができる.
utf-8は、1文字が1バイト、2バイト、3バイト、4バイト、最大6バイトで表される変長符号化文字セットである.各バイト長の符号化モードは以下の通りである.
バイト数
バイナリ
説明
1バイト
   0xxxxxxx
ASCII文字
2バイト
   110xxxxx  10xxxxxx
 
3バイト
   1110xxxx  10xxxxxx 10xxxxxx
漢字
4バイト
   11110xxx  10xxxxxx 10xxxxxx  10xxxxxx
 
5バイト
   111110xx  10xxxxxx 10xxxxxx  10xxxxxx 10xxxxxx
 
6バイト
   1111110x  10xxxxxx  10xxxxxx  10xxxxxx  10xxxxxx  10xxxxxx
 
上表の変長符号化モードがあり、雲風を組み合わせてまとめたブログ(http://blog.codingnow.com/2010/06/detect_utf-8_gbk.html)、基本的には次のような結論が得られます.
url中の非ascii文字は漢字にすぎず、非ascii文字に遭遇するとutf-8の3バイト符号化規則に従って識別され、マッチング上はutf-8の文字とみなされ、url中のすべての非asciiがutf-8の3バイト符号化モードとマッチングできる場合、この符号化はutf-8とみなされる.では、このurlをgbkに変換できます.この判定方法は明らかに簡単で乱暴であるが,ほとんどの場合を解決できるはずである.
雲風博文では、連続漢字数が3の倍数でなければ、gbkをUTF-8と誤認することはないと述べている.これはgbkが漢字を2バイトで符号化し,単バイトのascii符号化と区別するために最高位は必ず1であるため,これ以外は規則的ではない.つまり、GBKの漢字符号化パターンは、1 xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx検索キーワードが3つの漢字であると仮定すると、この3つの漢字のgbk符号化後のバイナリは次のように長くなります.
漢字1
漢字2
漢字3
1110xxxx  10xxxxxx
10xxxxxx  1110xxxx
10xxxxxx  10xxxxxx
上の表から明らかなように、3つの漢字gbk符号化後、UTF-8 3バイト符号化モードで2つの漢字と誤認される可能性があり、赤と青がそれぞれ新しい漢字を構成している.しかし、よく考えてみると、3つの漢字が同時にこのようなパターンに合致する確率は低いはずです.学術的な観点から言えば、この方法は確かに簡単で乱暴だが、工業生産の観点から見れば、実際の問題をよく解決できる方法がいい方法だ.最近ちょうどテストシステムが走っているので、これを入れてみて、実際の効果はどうなのか、99%の状況を処理できるかどうか見てみましょう.
urlがgbkであると仮定すると、まずutf-8に変換し、GBKに変換し、最後に元のurlとバイトを単語ごとに比較して、同じかどうかを確認する方法もあります.この方法は,urlを大量に処理する必要があるサーバとして,性能的に考慮する必要がある2回逆転することである.
コードを添付(訂正を歓迎):
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <stdint.h>
#include <sys/types.h>
#include <unistd.h>
#include <fcntl.h>

#define YES  0
#define NO  -1

#define ASCII__MASK     0x80
#define CHINESE_MASK1   0xF0
#define CHINESE_MASK2   0xC0
#define CHINESE_MASK3   0xC0

#define ASCII_VALUE     0x00
#define CHINESE_VALUE1  0xE0
#define CHINESE_VALUE2  0x80
#define CHINESE_VALUE3  0x80

int check_utf8_chinese(u_char *data, size_t len)
{
    u_char ch;
    size_t i, count, fail;

    enum {
        chinese1 = 0,
        chinese2,
        chinese3
    } state;

    state = chinese1;
    count = 0;
    fail = 0;

    for (i = 0; i < len; i++) {
        ch = *(data + i);
        if (ch <= 127) {
            continue;
        }

        switch (state) {
        case chinese1:
            if (!((ch & CHINESE_MASK1) ^ CHINESE_VALUE1)) {
                state = chinese2;
            } else {
                fail++;
            }
            break;
        case chinese2:
            if (!((ch & CHINESE_MASK2) ^ CHINESE_VALUE2)) {
                state = chinese3;
            } else {
                state = chinese1;
                fail++;
            }
            break;
        case chinese3:
            if (!((ch & CHINESE_MASK3) ^ CHINESE_VALUE3)) {
                count++;
            } else {
                fail++;
            }
            state = chinese1;
        }
    }

    return fail != 0 ? NO : YES;
}

int main(int argc, char **argv)
{
    int fd, n, ret;
    u_char buffer[1024];

    fd = open(argv[1], O_RDONLY);
    if (fd < 0) {
        perror("open");
    }

    n = read(fd, buffer, 1024);
    if (n < 0) {
        perror("read");
    }

    ret = check_utf8_chinese(buffer, n);
    printf("%s
", (ret == NO ? "gbk" : "utf-8")); return 0; }