havenask
havenask copied to clipboard
utf8编码字节数大于3的字符无法召回
用以下数据建库
CMD=add^_
id=1^_
query=𣄃的组词^_
^^
CMD=add^_
id=2^_
query=形容北京城^_
^^
CMD=add^_
id=3^_
query=怎么读𠀋这个字^_
^^
用“𣄃“,”𠀋“无法召回原文档。查看代码发现是Analyzer的normalize()函数内部会调用
int32_t EncodeConverter::utf8ToUtf16(const char *in, int32_t length, uint16_t *out) {
int32_t ret = 0;
const unsigned char *b = reinterpret_cast<const unsigned char *>(in);
const unsigned char *e = b + length;
bool error = false;
while (b < e) {
if (*b < 128) {
out[ret++] = *b++;
} else if ((*b & 0xE0) == 0xC0) {
uint16_t u = ((*b++) & (0x1F));
if (b < e && (*b & 0xC0) == 0x80) {
out[ret++] = (u << 6) | ((*b++) & 0x3F);
} else {
error = true;
}
} else if ((*b & 0xF0) == 0xE0) {
uint16_t u = ((*b++) & (0xF));
if (b < e && (*b & 0xC0) == 0x80) {
u = (u << 6) | ((*b++) & 0x3F);
if (b < e && (*b & 0xC0) == 0x80) {
out[ret++] = (u << 6) | ((*b++) & 0x3F);
} else {
error = true;
}
} else {
error = true;
}
} else {
error = true;
b++;
}
}
if (error) {
AUTIL_LOG(DEBUG, "invalid utf8 [%s]", std::string(in, length).c_str());
}
return ret;
}
这个函数并没有处理utf8字符为四字节的情况,而且在后续的NormalizeTable中也只分配了0x10000个uint16_t的空间,因此只能处理Unicode码小于0x10000的字符。
如果文档包含Unicode码大于0x10000(utf8编码字节数大于3)的字符,需要把analyzer.json里面的normalize_options都设置为true,让代码不走normalize逻辑,但是大小写、全半角、繁简体的转换就得预先处理了。
"normalize_options" :
{
"case_sensitive" : false,
"traditional_sensitive" : true,
"width_sensitive" : false
}
这个有fix的方案吗?
我们先确认下
确实存在这个问题,只支持对常用的文字做normalize,fix的方案还要好好梳理下,你这边只能先关闭normalize选项,在外部normalize。我们先记录这个问题,再下一个版本尝试修复
或者你是否可以修复一下,我们merge进来,主要的代码逻辑在autil/autil/codec/下面