首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >utf8感知strncpy

utf8感知strncpy
EN

Stack Overflow用户
提问于 2011-09-08 07:39:29
回答 5查看 6.6K关注 0票数 10

我很难相信我是第一个遇到这个问题的人,但是我找了很长一段时间,却没有找到解决这个问题的办法。

我想使用strncpy,但是让它是UTF8感知的,这样它就不会部分地将utf8字符写入目标字符串。

否则,即使您知道源字符串是有效的(当源字符串大于最大长度时),也永远无法确保得到的字符串是有效的UTF8。

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

glib有g_utf8_strncpy,但是它复制了一定数量的unicode字符,而我在寻找一个限制字节长度的复制函数。

要明确的是,通过"utf8感知“,我的意思是它不应该超过目标缓冲区的限制,而且它必须从不只复制utf-8字符的一部分。(给定有效的utf-8输入,绝不能导致无效的utf-8输出)。

注意:

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

EN

回答 5

Stack Overflow用户

回答已采纳

发布于 2011-09-15 13:58:07

为了回答自己的问题,下面是我最后得到的C函数(这个项目不使用C++ ):

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

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

感谢James,他提供了这方面的基础,但不完整和C++ (我需要一个C版本)。

代码语言:javascript
复制
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;
}
票数 3
EN

Stack Overflow用户

发布于 2015-01-08 04:05:57

我已经在许多带有多字节字符的示例UTF8字符串上测试了这一点。如果源太长,则对其进行反向搜索(从null终止符开始),并向后工作以找到最后一个适合于目标缓冲区的完整UTF8字符。它总是确保目标为null。

代码语言:javascript
复制
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;
}
票数 9
EN

Stack Overflow用户

发布于 2011-09-08 08:05:05

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

代码语言:javascript
复制
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;
}

(在utf8Size中生成表的内容有点痛苦,但是如果您处理UTF-8,并且只需要做一次,那么这是一个需要大量使用的函数。)

票数 7
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/7344683

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档