代码之家  ›  专栏  ›  技术社区  ›  vy32

如果Unicode无效(C/C++),则验证Unicode字符串并逃出

  •  3
  • vy32  · 技术社区  · 14 年前

    我有一个从文件系统读取任意数据并以Unicode输出结果的程序。我的问题是,有时文件名是有效的Unicode,有时它们不是。所以我想要一个可以验证字符串(C或C++)的函数,并告诉我它是否是一个有效的UTF-8编码。如果不是,我希望对无效字符进行转义,以便它是有效的UTF-8编码。这与为XML转义不同——我也需要这样做。但首先,我需要确保Unicode是正确的。

    我已经看到了一些代码,我可以从中破解这些代码,但如果它存在的话,我宁愿使用一些工作代码。

    2 回复  |  直到 14 年前
        1
  •  1
  •   Remy Lebeau    14 年前

    以下代码基于 IRI 我在图书馆工作了一段时间。第3.2节(将URI转换为IRIS) RFC 3987 处理将无效的UTF-8八位字节转换为有效的UTF-8。

    #define IS_IN_RANGE(c, f, l)    (((c) >= (f)) && ((c) <= (l)))
    
    int UTF8BufferToUTF32Buffer(char *Data, int DataLen, unsigned long *Buffer, int BufLen, int *Eaten)
    {
        if( Eaten )
        {
            *Eaten = 0;
        }
    
        int Result = 0;
    
        unsigned char b, b2;
        unsigned char *ptr = (unsigned char*) Data;
        unsigned long uc;
    
        int i = 0;
        int seqlen;
    
        while( i < DataLen )
        {
            if( (Buffer) && (!BufLen) )
                break;
    
            b = ptr[i];
    
            if( (b & 0x80) == 0 )
            {
                uc = (unsigned long)(b & 0x7F);
                seqlen = 1;
            }
            else if( (b & 0xE0) == 0xC0 )
            {
                uc = (unsigned long)(b & 0x1F);
                seqlen = 2;
            }
            else if( (b & 0xF0) == 0xE0 )
            {
                uc = (unsigned long)(b & 0x0F);
                seqlen = 3;
            }
            else if( (b & 0xF8) == 0xF0 )
            {
                uc = (unsigned long)(b & 0x07);
                seqlen = 4;
            }
            else
            {
                uc = 0;
                return -1;
            }
    
            if( (i+seqlen) > DataLen )
            {
                return -1;
            }
    
            for(int j = 1; j < seqlen; ++j)
            {
                b = ptr[i+j];
    
                if( (b & 0xC0) != 0x80 )
                {
                    return -1;
                }
            }
    
            switch( seqlen )
            {
                case 2:
                {
                    b = ptr[i];
    
                    if( !IS_IN_RANGE(b, 0xC2, 0xDF) )
                    {
                        return -1;
                    }
    
                    break;
                }
    
                case 3:
                {
                    b = ptr[i];
                    b2 = ptr[i+1];
    
                    if( ((b == 0xE0) && !IS_IN_RANGE(b2, 0xA0, 0xBF)) ||
                        ((b == 0xED) && !IS_IN_RANGE(b2, 0x80, 0x9F)) ||
                        (!IS_IN_RANGE(b, 0xE1, 0xEC) && !IS_IN_RANGE(b, 0xEE, 0xEF)) )
                    {
                        return -1;
                    }
    
                    break;
                }
    
                case 4:
                {
                    b = ptr[i];
                    b2 = ptr[i+1];
    
                    if( ((b == 0xF0) && !IS_IN_RANGE(b2, 0x90, 0xBF)) ||
                        ((b == 0xF4) && !IS_IN_RANGE(b2, 0x80, 0x8F)) ||
                        !IS_IN_RANGE(b, 0xF1, 0xF3) )
                    {
                        return -1;
                    }
    
                    break;
                }
            }
    
            for(int j = 1; j < seqlen; ++j)
            {
                uc = ((uc << 6) | (unsigned long)(ptr[i+j] & 0x3F));
            }
    
            if( Buffer )
            {
                *Buffer++ = uc;
                --BufLen;
            }
    
            ++Result;
            i += seqlen;
        }
    
        if( Eaten )
        {
            *Eaten = i;
        }
    
        return Result;
    }
    
    {
        std::string filename = "...";
    
        unsigned long ch;
        int eaten;
    
        std::string::size_type i = 0;
        while( i < filename.length() )
        {
            if( UTF8BufferToUTF32Buffer(&filename[i], filename.length()-i, &ch, 1, &eaten) == 1 )
            {
                i += eaten;
            }
            else
            {
                // replace the character at filename[i] with your chosen
                // escaping, and then increment i by the number of
                // characters used...
            }
        }
    }
    

    在您的情况下,您所要做的就是决定要使用哪种类型的转义。uri/iris使用百分比编码(“%nn”,其中“nn”是八位字节的2位十六进制值)。

        2
  •  0
  •   dan04    14 年前

    如果您有字符编码转换函数(如 MultiByteToWideChar WideCharToMultiByte 在Windows上),您可以尝试将字符串从utf-8转换为utf-16/32,然后再重新转换。如果原始字符串是utf-8,那么您将得到相同的字符串。如果没有,您将得到一个错误(如果您只想进行有效性检查,这很有用),或者将无效的UTF-8字节替换为有效的(这是您最终想要的)。