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,但当时我不知道这个函数是否存在.
我不确定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,那么这个函数你将会使用很多,而且你只需要执行一次.)
我已经在许多具有多字节字符的样本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)
为了回答自己的问题,这是我最终得到的 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)