也谈Windows记事本的BUG

标签: , , ,

在Windows操作系统中使用记事本新建一个文本文件,在文件里面写入“联通”两个字并保存。当再次打开这个文本文件时候,在记事本中看到得却不是刚刚输入的“联通”,而是乱码。这就是移动比联通强的原因!

这是网上广为流传的笑话,也有不少人分析过其中的原因:“联通”这两个字符的GBK编码具有UTF-8编码的特征,记事本犯下的错误正是将GBK编码存放的记录有“联通”两个字符的文件误认为UTF-8编码的文件。

这虽然没有错,但是无代码无真相,记事本是如何判断一个文件的编码是不是UTF-8的呢?用OllyDbg调试了一下,判断UTF-8的过程如下:

010098C7 >/$  8BFF          mov     edi, edi
010098C9  |.  55            push    ebp
010098CA  |.  8BEC          mov     ebp, esp
010098CC  |.  56            push    esi
010098CD  |.  33F6          xor     esi, esi
010098CF  |.  33C9          xor     ecx, ecx
010098D1  |.  46            inc     esi
010098D2  |.  33D2          xor     edx, edx
010098D4  |.  394D 0C       cmp     dword ptr [ebp+C], ecx
010098D7  |.  7E 1E         jle     short 010098F7
010098D9  |>  8B45 08       mov     eax, dword ptr [ebp+8]
010098DC  |.  8A0401        mov     al, byte ptr [ecx+eax]
010098DF  |.  84C0          test    al, al
010098E1  |.  79 02         jns     short 010098E5
010098E3  |.  33F6          xor     esi, esi
010098E5  |>  85D2          test    edx, edx
010098E7  |.  75 12         jnz     short 010098FB
010098E9  |.  3C 80         cmp     al, 80
010098EB  |.  72 15         jb      short 01009902
010098ED  |>  02C0          /add     al, al
010098EF  |.  42            |inc     edx
010098F0  |.  84C0          |test    al, al
010098F2  |.^ 78 F9         \js      short 010098ED
010098F4  |.  4A            dec     edx
010098F5  |.  75 0B         jnz     short 01009902
010098F7  |>  33C0          xor     eax, eax
010098F9  |.  EB 18         jmp     short 01009913
010098FB  |>  24 C0         and     al, 0C0
010098FD  |.  3C 80         cmp     al, 80
010098FF  |.^ 75 F6         jnz     short 010098F7
01009901  |.  4A            dec     edx
01009902  |>  41            inc     ecx
01009903  |.  3B4D 0C       cmp     ecx, dword ptr [ebp+C]
01009906  |.^ 7C D1         jl      short 010098D9
01009908  |.  85D2          test    edx, edx
0100990A  |.^ 77 EB         ja      short 010098F7
0100990C  |.  85F6          test    esi, esi
0100990E  |.^ 75 E7         jnz     short 010098F7
01009910  |.  33C0          xor     eax, eax
01009912  |.  40            inc     eax
01009913  |>  5E            pop     esi
01009914  |.  5D            pop     ebp
01009915  \.  C2 0800       retn    8

看不懂汇编?没关系,其实我也看不懂,翻译成C代码大概是这样:

/***************************************
By Demon
http://demon.tw
***************************************/
int is_utf8(unsigned char buf, int len)
{
    int i, ascii_only, continuation_bytes;
    unsigned char c;

    i = 0;
    ascii_only = 1;
    continuation_bytes = 0;

    if ( len <= 0 )
        return 0;

    do
    {
        c = *(buf + i);
        if ( (signed char)c < 0 )
            ascii_only = 0;

        if ( continuation_bytes )
        {
            if ( (c & 0xC0) != 0x80u )
                return 0;
            --continuation_bytes;
        }
        else
        {
            if ( c >= 0x80u )
            {
                do
                {
                    c <<= 1;
                    ++continuation_bytes;
                }
                while ( (signed char)c < 0 );
                --continuation_bytes;

                if ( !continuation_bytes )
                    return 0;
            }
        }
        ++i;
    }
    while ( i < len );

    if ( continuation_bytes || ascii_only )
        return 0;

    return 1;
}

可见,只要是符合UTF-8编码特征的字节流都会被记事本认为是UTF-8编码,而不论其被正确的编码了。例如“联通”的“联”的GBK编码为C1AA,转成二进制是:

C1 11000001
AA 10101010

红色部分为UTF-8的特征,把剩余部分00001和101010连起来是00001101010,即十六进制6A,但是6A在UTF-8里只需要一个字节,而不是两个。所以C1AA其实并不是有效的UTF-8字节流,虽然它表面上符合UTF-8的特征,而记事本却没有做进一步的判断。

参考链接:http://en.wikipedia.org/wiki/Utf8

随机文章:

  1. 在VC中编译运行程序的小知识点
  2. 用VBS实现凯撒密码算法
  3. 3DS导出正版卡带存档并导入到CIA
  4. 不用循环计算1到100的和
  5. VBS显示桌面

5 条评论 发表在“也谈Windows记事本的BUG”上

  1. […] « 也谈Windows记事本的BUG […]

  2. 开源混蛋说道:

    这bug恐怕在Win7上无法重现了吧……还有Windows下的文本编辑器用Notepad++吧……

    • 11说道:

      这就是典型的脑袋长在屁股上的说的话,不实践就瞎BB,不好意思,WIN7的同样有这个BUG

  3. 22说道:

    server 2012 r2 目前依然存在这个问题

  4. QinWi说道:

    notepad++ 貌似有同样问题哦,在默认设置下

留下回复