为什么使用 locale-character 时打印的字符串更短
Why printed string is shorter when locale-charater is used
我写了下面的代码。我正在尝试使用非 ASCII 字符打印指定长度的字符串。
int main(int argc, char **argv)
{
setlocale(LC_ALL,"pl_PL");
printf("%-10sx\n","ą");
printf("%-10sx\n","a");
return 0;
}
输出结果如下:
ą x
a x
使用非ASCII字符时少一个(白色)space。为什么会这样?
Why printed string is shorter when locale-charater is used
因为表示一个multi-byte字符串所需的列数不等于字符占用的字节数。
Why does it behave like this?
字符串 "ą"
占用 2 个字节(零终止字符还占用 1 个字节),但显示在 1 列上。所以会有8个空格的padding。
字符串"a
"的长度为1个字节,所以会有9个空格填充。
Is there any way to overcome this issue without manually changing the desired length when the string contains a non-ASCII character?
使用一个库,该库包含字符及其宽度之间的映射数据库,用于您正在使用的编码。遍历字符串,获取表示它所需的列数。然后将显示的宽度添加到您希望从该长度获得的偏移量。总的来说,显示字符的宽度是一项 non-trivial 任务,并且存在问题和边缘情况。
使用 https://www.joelonsoftware.com/2003/10/08/the-absolute-minimum-every-software-developer-absolutely-positively-must-know-about-unicode-and-character-sets-no-excuses/ . Read about normal, multi-byte and wide stings and about uchar8_t
uchar16_t
and uchar32_t
strings - https://en.cppreference.com/w/c/string/wide and https://en.cppreference.com/w/c/string/multibyte 进入 unicode 世界。
在 Linux 上,您的语言环境默认使用 UTF-8,您的终端很可能使用 UTF-8,并且您的编译器对字符串文字使用 UTF-8 编码(这些都是单独的属性和可以混合)。在 Linux 上,您可以将字符串转换为宽字符串(它本身很难)并遍历字符串并使用 wcswidth
获取列数。还有图书馆 - libunistring
具有 u8_width
功能,ICU 具有 u_countChar32
和类似功能。
我能看到一些东西:
#define _XOPEN_SOURCE // for wcwidth on Linux
#include <wchar.h>
#include <assert.h>
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include <limits.h>
#include <stdint.h>
#include <locale.h>
size_t mbswidth(const char *s, size_t n) { // similar to wcswidth
mbstate_t ps;
memset(&ps, 0, sizeof(ps));
size_t ret = 0;
while (n != 0 && *s != 0) {
wchar_t wc;
const size_t rr = mbrtowc(&wc, s, n, &ps);
if (rr == (size_t)-1 || rr == (size_t)-2) {
return rr;
}
assert(rr != 0);
n -= rr;
s += rr;
ret += wcwidth(wc);
}
return ret;
}
int main() {
setlocale(LC_ALL, "pl_PL.UTF-8"); // see https://www.gnu.org/software/libc/manual/html_node/Locale-Names.html
const char *s = "ą"; // I think I would `= u8"ą";` on newer compilers
printf("%-*sx\n", 10 + (int)mbswidth(s, SIZE_MAX), s);
printf("%-*sx\n", 10, "a");
}
我写了下面的代码。我正在尝试使用非 ASCII 字符打印指定长度的字符串。
int main(int argc, char **argv)
{
setlocale(LC_ALL,"pl_PL");
printf("%-10sx\n","ą");
printf("%-10sx\n","a");
return 0;
}
输出结果如下:
ą x
a x
使用非ASCII字符时少一个(白色)space。为什么会这样?
Why printed string is shorter when locale-charater is used
因为表示一个multi-byte字符串所需的列数不等于字符占用的字节数。
Why does it behave like this?
字符串 "ą"
占用 2 个字节(零终止字符还占用 1 个字节),但显示在 1 列上。所以会有8个空格的padding。
字符串"a
"的长度为1个字节,所以会有9个空格填充。
Is there any way to overcome this issue without manually changing the desired length when the string contains a non-ASCII character?
使用一个库,该库包含字符及其宽度之间的映射数据库,用于您正在使用的编码。遍历字符串,获取表示它所需的列数。然后将显示的宽度添加到您希望从该长度获得的偏移量。总的来说,显示字符的宽度是一项 non-trivial 任务,并且存在问题和边缘情况。
使用 https://www.joelonsoftware.com/2003/10/08/the-absolute-minimum-every-software-developer-absolutely-positively-must-know-about-unicode-and-character-sets-no-excuses/ . Read about normal, multi-byte and wide stings and about uchar8_t
uchar16_t
and uchar32_t
strings - https://en.cppreference.com/w/c/string/wide and https://en.cppreference.com/w/c/string/multibyte 进入 unicode 世界。
在 Linux 上,您的语言环境默认使用 UTF-8,您的终端很可能使用 UTF-8,并且您的编译器对字符串文字使用 UTF-8 编码(这些都是单独的属性和可以混合)。在 Linux 上,您可以将字符串转换为宽字符串(它本身很难)并遍历字符串并使用 wcswidth
获取列数。还有图书馆 - libunistring
具有 u8_width
功能,ICU 具有 u_countChar32
和类似功能。
我能看到一些东西:
#define _XOPEN_SOURCE // for wcwidth on Linux
#include <wchar.h>
#include <assert.h>
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include <limits.h>
#include <stdint.h>
#include <locale.h>
size_t mbswidth(const char *s, size_t n) { // similar to wcswidth
mbstate_t ps;
memset(&ps, 0, sizeof(ps));
size_t ret = 0;
while (n != 0 && *s != 0) {
wchar_t wc;
const size_t rr = mbrtowc(&wc, s, n, &ps);
if (rr == (size_t)-1 || rr == (size_t)-2) {
return rr;
}
assert(rr != 0);
n -= rr;
s += rr;
ret += wcwidth(wc);
}
return ret;
}
int main() {
setlocale(LC_ALL, "pl_PL.UTF-8"); // see https://www.gnu.org/software/libc/manual/html_node/Locale-Names.html
const char *s = "ą"; // I think I would `= u8"ą";` on newer compilers
printf("%-*sx\n", 10 + (int)mbswidth(s, SIZE_MAX), s);
printf("%-*sx\n", 10, "a");
}