代码之家  ›  专栏  ›  技术社区  ›  Frank-Rene Schäfer

ICU:ucnv_convertEx-实时检测编码错误

  •  2
  • Frank-Rene Schäfer  · 技术社区  · 6 年前

    是否有可能在转换时用icu检测编码错误,或者有必要在转换之前或之后检查编码错误?

    在设置从utf8到utf32的转换的初始化时:

    #include <stdio.h>
    #include "unicode/ucnv.h"     /* C   Converter API    */
    
    static void eval(UConverter* from, UConverter* to);
    
    int main(int argc, char** argv) {
        UConverter*  from;
        UConverter*  to;
        UErrorCode   status;
    
        /* Initialize converter from UTF8 to Unicode _____________________________*/
        status = U_ZERO_ERROR;
        from   = ucnv_open("UTF-8", &status);
        if( ! from || ! U_SUCCESS(status) ) return 1; 
        status = U_ZERO_ERROR;
        to     = ucnv_open("UTF32", &status);
        if( ! to || ! U_SUCCESS(status) ) return 1; 
        /*________________________________________________________________________*/
    
        eval(from, to);
        return 0;
    }
    

    然后,使用 ucnv_convertEx 通过

    static void eval(UConverter* from, UConverter* to) 
    {
        UErrorCode  status = U_ZERO_ERROR;
        uint32_t    drain[1024];
        uint32_t*   drain_p = &drain[0];
        uint32_t*   p       = &drain[0];
    
        /* UTF8 sequence with error in third byte ________________________________*/
        const char  source[] = { "\xED\x8A\x0A\x0A" }; 
        const char* source_p = &source[0];
    
        ucnv_convertEx(to, from, (char**)&drain_p, (char*)&drain[1024],
                       &source_p, &source[5],
                       NULL, NULL, NULL, NULL, /* reset = */TRUE, /* flush = */TRUE,
                       &status);
    
        /* Print conversion result _______________________________________________*/
        printf("source_p: source + %i;\n", (int)(source_p - &source[0]));
        printf("status:   %s;\n", u_errorName(status));
        printf("drain:    (n=%i)[", (int)(drain_p - &drain[0]));
        for(p=&drain[0]; p != drain_p ; ++p) { printf("%06X ", (int)*p); }
        printf("]\n");
    }
    

    如果“source”包含不可接受的UTF8代码单元序列,则函数应该以某种方式报告错误将上述片段存储在“test.c”中,并使用

    gcc test.c$(icu config--ldflags)-o测试

    的输出 ./test 令人惊讶的是:

    source_p: source + 5;
    status:   U_ZERO_ERROR;
    drain:    (n=5)[00FEFF 00FFFD 00000A 00000A 000000 ]
    

    所以,没有明显的错误迹象错误检测能比手动检查内容更优雅吗?

    1 回复  |  直到 6 年前
        1
  •  1
  •   Peter Torr    6 年前

    正如@Eljay在评论中所建议的,您可以使用错误回调你甚至不需要自己写,因为 UCNV_TO_U_CALLBACK_STOP 会做你想做的事(即,对任何坏角色返回失败)。

    int TestIt()
    {
      UConverter* utf8conv{};
      UConverter* utf32conv{};
      UErrorCode status{ U_ZERO_ERROR };
    
      utf8conv = ucnv_open("UTF8", &status);
    
      if (!U_SUCCESS(status))
      {
        return 1;
      }
    
      utf32conv = ucnv_open("UTF32", &status);
    
      if (!U_SUCCESS(status))
      {
        return 2;
      }
    
      const char source[] =  { "\xED\x8A\x0A\x0A" };
      uint32_t target[10]{ 0 };
    
      ucnv_setToUCallBack(utf8conv, UCNV_TO_U_CALLBACK_STOP, nullptr, 
        nullptr, nullptr, &status);
    
      if (!U_SUCCESS(status))
      {
        return 3;
      }
    
      auto sourcePtr = source;
      auto sourceEnd = source + ARRAYSIZE(source);
      auto targetPtr = target;
      auto targetEnd = reinterpret_cast<const char*>(target + ARRAYSIZE(target));
    
      ucnv_convertEx(utf32conv, utf8conv, reinterpret_cast<char**>(&targetPtr),
        targetEnd, &sourcePtr, sourceEnd, nullptr, nullptr, nullptr, nullptr, 
        TRUE, TRUE, &status);
    
      if (!U_SUCCESS(status))
      {
        return 4;
      }
    
      printf("Converted '%s' to '", source);
      for (auto start = target; start != targetPtr; start++)
      {
        printf("\\x%x", *start);
      }
      printf("'\r\n");
    
      return 0;
    }
    

    这应该会回来 4 对于无效的Unicode代码点,如果成功,则打印出UTF-32值。我们不太可能从 ucnv_setToUCallBack ,但我们会检查以防万一在上面的例子中,我们通过了 nullptr 因为我们不在乎它是什么,也不需要重置它。