字符串使用的字符单元格数

cod*_*pet 25 c linux string utf-8

我有一个程序,使用UTF-8字符串输出文本表,我需要测量字符串使用的等宽字符单元格的数量,以便我可以正确对齐它.如果可能的话,我想用标准功能做到这一点.

Max*_*kin 33

来自Unix/Linux的UTF-8和Unicode FAQ:

可以使用便携式方式在C中计算字符数mbstowcs(NULL,s,0).只要选择了适当的语言环境,这适用于UTF-8,就像任何其他支持的编码一样.计算UTF-8字符串中字符数的硬连线技术是计算除0x80 - 0xBF范围内的所有字节,因为这些只是连续字节而不是它们自己的字符.然而,在应用程序中很少出现计算字符的需要.

  • 请记住,如果涉及组合字符,计数代码点将给出错误的答案; 甚至normalizint输入也无济于事,因为有些字母没有映射到单个代码点... (27认同)
  • 在对齐的背景下(根据问题),同一文件说字符数量不足:"字节和字符数都不能预测显示宽度,因为表意字符(中文,日文,韩文)将占用两个列位置,而控制和组合字符不占用.要确定终端屏幕上字符串的宽度,必须解码UTF-8序列,然后使用wcwidth函数测试每个字符的显示宽度,或wcswidth测量整个字符串." (2认同)

mpe*_*ez0 21

您可能有也可能没有UTF-8兼容的strlen(3)功能.但是,有一些简单的C函数可以快速完成工作.

高效的C解决方案检查字符的开头是否跳过连续字节.简单的代码(从上面的链接引用)是

int my_strlen_utf8_c(char *s) {
   int i = 0, j = 0;
   while (s[i]) {
     if ((s[i] & 0xc0) != 0x80) j++;
     i++;
   }
   return j;
}
Run Code Online (Sandbox Code Playgroud)

较快的版本使用相同的技术,但预取数据并进行多字节比较,从而产生了大量的加速.但是,代码更长,更复杂.

  • strlen(3)计数*字节*. (10认同)

Fun*_*ino 6

我很震惊,没有人提到这一点,所以这里有记录:

如果要在终端中对齐文本,则需要使用POSIX函数wcwidthwcswidth.这是找到字符串的屏幕长度的正确程序.

#define _XOPEN_SOURCE
#include <wchar.h>
#include <stdio.h>
#include <locale.h>
#include <stdlib.h>

int measure(char *string) {
    // allocate enough memory to hold the wide string
    size_t needed = mbstowcs(NULL, string, 0) + 1;
    wchar_t *wcstring = malloc(needed * sizeof *wcstring);
    if (!wcstring) return -1;

    // change encodings
    if (mbstowcs(wcstring, string, needed) == (size_t)-1) return -2;

    // measure width
    int width = wcswidth(wcstring, needed);

    free(wcstring);
    return width;
}

int main(int argc, char **argv) {
    setlocale(LC_ALL, "");

    for (int i = 1; i < argc; i++) {
        printf("%s: %d\n", argv[i], measure(argv[i]));
    }
}
Run Code Online (Sandbox Code Playgroud)

以下是它运行的示例:

$ ./measure hello ?? c?b
hello: 5
??: 4
c?b: 4
Run Code Online (Sandbox Code Playgroud)

注意两个字符"庄子"和三个字符"cAb"(注意双宽度A)都是4列宽.

正如utf8everywhere.org 所说,

屏幕上显示的字符串大小与字符串中的代码点数无关.为此,必须与渲染引擎进行通信.即使在等宽字体和终端中,代码点也不占用一列.POSIX考虑到了这一点.

Windows没有任何内置的wcwidth控制台输出功能; 如果你想在Windows控制台中支持多列字符,你需要找到一个可wcwidth放弃的可移植实现,因为Windows控制台不支持Unicode而没有疯狂的黑客攻击.