utf8意识到strncpy

ide*_*n42 8 c c++ utf-8 strncpy

我发现很难相信我是遇到这个问题的第一个人,但是搜索了很长时间并没有找到解决方案.

我想使用strncpy但是它具有UTF8意识,因此它不会将utf8字符部分写入目标字符串.

否则,您永远无法确定结果字符串是否为有效的UTF8,即使您知道源是(当源字符串大于最大长度时).

验证生成的字符串可以工作,但如果要调用它,最好有一个strncpy函数来检查它.

glib有,g_utf8_strncpy但这会复制一定数量的unicode字符,而我正在寻找一个限制字节长度的复制函数.

要清楚,通过"utf8 aware",我的意思是它不应超过目标缓冲区的限制,并且它绝不能只复制utf-8字符的一部分.(给定有效的utf-8输入必须永远不会导致utf-8输出无效).


注意:

一些回复指出,strncpy所有字节都为空,并且它不会确保零终止,回想起来我应该要求知道utf8 strlcpy,但当时我不知道这个函数是否存在.

Jam*_*nze 6

我不确定UTF-8的意思是什么意思; strncpy复制字节,而不是字符,缓冲区的大小也以字节为单位.如果你的意思是它只会复制完整的UTF-8字符,例如停止,如果没有下一个字符的空间,我不知道这样的功能,但它不应该太难来写:

int
utf8Size( char ch )
{
    static int const sizeTable[] =
    {
        //  ...
    };
    return sizeTable( static_cast<unsigned char>( ch ) )
}

char*
stru8ncpy( char* dest, char* source, int n )
{
    while ( *source != '\0' && utf8Size( *source ) < n ) {
        n -= utf8Size( *source );
        switch ( utf8Size( ch ) ) {
        case 6:
            *dest ++ = *source ++;
        case 5:
            *dest ++ = *source ++;
        case 4:
            *dest ++ = *source ++;
        case 3:
            *dest ++ = *source ++;
        case 2:
            *dest ++ = *source ++;
        case 1:
            *dest ++ = *source ++;
            break;
        default:
            throw IllegalUTF8();
        }
    }
    *dest = '\0';
    return dest;
}
Run Code Online (Sandbox Code Playgroud)

(utf8Size中表的内容生成起来有点痛苦,但是如果你正在处理UTF-8,那么这个函数你将会使用很多,而且你只需要执行一次.)

  • Unicode字符的UTF-8表示永远不会超过4个字节.早期的提案指定了5字节和6字节序列,但现代UTF-8最高为4字节. (2认同)

Big*_* Al 6

我已经在许多具有多字节字符的样本UTF8字符串上进行了测试.如果源太长,它会对它进行反向搜索(从null终止符开始)并向后工作以查找可以放入目标缓冲区的最后一个完整UTF8字符.它始终确保目标为空终止.

char* utf8cpy(char* dst, const char* src, size_t sizeDest )
{
    if( sizeDest ){
        size_t sizeSrc = strlen(src); // number of bytes not including null
        while( sizeSrc >= sizeDest ){

            const char* lastByte = src + sizeSrc; // Initially, pointing to the null terminator.
            while( lastByte-- > src )
                if((*lastByte & 0xC0) != 0x80) // Found the initial byte of the (potentially) multi-byte character (or found null).
                    break;

            sizeSrc = lastByte - src;
        }
        memcpy(dst, src, sizeSrc);
        dst[sizeSrc] = '\0';
    }
    return dst;
}
Run Code Online (Sandbox Code Playgroud)


ide*_*n42 3

为了回答自己的问题,这是我最终得到的 C 函数(在这个项目中没有使用 C++):

注意: - 意识到这不是strncpyutf8 的克隆,它更像strlcpy来自 openbsd。- 从 glib 的 gutf8.c 复制的 utf8_skip_data - 它不验证 utf8 - 这就是我的意图。

希望这对其他人有用并且对反馈感兴趣,但请不要对NULL终止行为抱有迂腐的狂热,除非它是一个实际的错误,或误导/不正确的行为。

感谢 James Kanze 为此提供了基础,但不完整且是 C++(我需要 C 版本)。

static const size_t utf8_skip_data[256] = {
    1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
    1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
    1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
    1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
    1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
    1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
    2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,
    3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,4,4,4,4,4,4,4,4,5,5,5,5,6,6,1,1
};

char *strlcpy_utf8(char *dst, const char *src, size_t maxncpy)
{
    char *dst_r = dst;
    size_t utf8_size;

    if (maxncpy > 0) {
        while (*src != '\0' && (utf8_size = utf8_skip_data[*((unsigned char *)src)]) < maxncpy) {
            maxncpy -= utf8_size;
            switch (utf8_size) {
                case 6: *dst ++ = *src ++;
                case 5: *dst ++ = *src ++;
                case 4: *dst ++ = *src ++;
                case 3: *dst ++ = *src ++;
                case 2: *dst ++ = *src ++;
                case 1: *dst ++ = *src ++;
            }
        }
        *dst= '\0';
    }
    return dst_r;
}
Run Code Online (Sandbox Code Playgroud)

  • 您假设是无符号字符。许多实现默认使用签名字符。 (2认同)