使用ICU实现我自己的编解码面

Using ICU to implement my own codecvt facet

本文关键字:解码 自己的 我自己 ICU 实现 使用      更新时间:2023-10-16

我想实现一个codecvt facet使用ICU从任何字符编码(ICU支持)转换到UTF-8内部。我知道codecvt_byname存在,并且可以使用它来完成本例中所示的部分任务。这个例子的问题是它(1)使用宽字符流(我想使用"常规",面向字节的流)和(2)需要2个流来执行转换。相反,我想要一个像

这样的单一流:
locale loc( locale(), new icu_codecvt( "ISO-8859-1" ) );
ifstream ifs;
ifs.imbue( loc );
ifs.open( "/path/to/some/file.txt" );
// data read from ifs here will have been converted from ISO-8859-1 to UTF-8

因此,我想做这样的实现,但使用ICU而不是iconv。鉴于此,我对do_in()的实现是:

icu_codecvt::result icu_codecvt::do_in( state_type &state,
                                        extern_type const *from, extern_type const *from_end,
                                        extern_type const *&from_next, intern_type *to,
                                        intern_type *to_end, intern_type *&to_next ) const {
  from_next = from;
  to_next = to;
  if ( always_noconv_ )
    return noconv;
  our_state *const s = state_store_.get( state );
  UErrorCode err = U_ZERO_ERROR;
  ucnv_convertEx(
    s->utf8_conv_, s->extern_conv_, &to_next, to_end, &from_next, from_end,
    nullptr, nullptr, nullptr, nullptr, false, false, &err
  );
  if ( err == U_TRUNCATED_CHAR_FOUND )
    return partial;
  return U_SUCCESS( err ) ? ok : error;
}

our_state对象维护两个UConverter*指针,一个用于"外部"编码(在本例中为ISO-8859-1),另一个用于UTF-8编码。

我的问题是:

  1. 我应该像上面那样为"枢轴"缓冲区指定nullptr,还是提供我自己的?
  2. 我不确定何时,如果有的话,我应该将reset参数(目前上面的第一个false)设置为true
  3. 我不清楚如何知道何时将flush参数(目前是上面的第二个false)设置为true,即,我如何知道何时达到输入的结束。

一点帮助?

codecvt facet 不是用于在不同编码之间进行转换。相反,它将一个字符可能使用多个外部单词(通常是字节)编码的外部编码转换为一个内部表示,其中每个字符仅由一个单词表示(例如char, wchar_t, char16_t等)。

从这个角度来看,"结束"内部字符序列是没有意义的。如果没有更多的外部词可用,则完成转换,如果最后一个字符仍然不完整,则在传输中出现错误。因此,没有必要表明转换已经完成,相应地也没有接口。这就澄清了"flush"实参确实应该总是为"false"。

我意识到UTF-8并不完全符合一个单词代表一个字符的要求。但是,这将困扰您使用标准类型处理字符串的整个UTF-8处理过程。不过,只要你不修改注射器,通常情况下都可以正常工作。

"reset"形参可能是用来处理流中的查找。我认为filebuf应该在寻找时提供一个新的state_type对象。这可能表明ICU内部人员想要重置。但是,我不知道ICU的接口。因此,我也不知道您是否想要提供一个pivot缓冲区。