如何在C语言中正确处理非ASCII字符串?

How to handle non-ASCII strings properly in C?

提问人:ᛉᛉᛉ ᛉᛉᛉ 提问时间:7/3/2023 最后编辑:tchristᛉᛉᛉ ᛉᛉᛉ 更新时间:7/3/2023 访问量:184

问:

我的想法是用 C 编写一个类似 Hangman 的游戏。我希望它能够使用带有变音符号的德语单词(例如:、、)和希腊语单词(完全非 ASCII 字符)。äüö

我的编译器和终端可以很好地处理Unicode。显示字符串效果很好。

但是我应该如何对这些字符串进行操作呢?对于德语,我也许可以通过在函数中处理这些情况来处理 6 个大写和小写重音字符。但考虑到希腊语,这似乎是不可能的。

我写了这个测试代码。它输出字符串、字符串的长度(当然是错误的,因为 UTF-8 序列代替了两个字符)以及字符串的单个字符的值(纯文本和十六进制)。

#include <stdio.h>
#include <string.h>

int main() {
    printf("123456789\n");
    char aTestString[] = "cheese";
    printf("%s ist %d Zeichen lang\n", aTestString, strlen(aTestString));
        
    for (int i = 0; i < strlen(aTestString); i++) {
        printf("( %c )", aTestString[i]);   // char als char
        printf("[ %02X ]", aTestString[i]); // char in hexadezimal
    }

    printf("\n123456789\n");
    char aTestString2[] = "Käse";
    printf("%s has %d characters\n", aTestString2, strlen(aTestString2));
        
    for (int i = 0; i < strlen(aTestString2); i++) {
        printf("( %c )", aTestString2[i]);  // char als char
        printf("[ %02X ]", aTestString2[i]); // char in hexadezimal
    }
    
    printf("\n123456789\n");    
    char aTestString3[] = "λόγος";
    printf("%s has %d characters\n", aTestString3, strlen(aTestString3));

    for (int i = 0; i < strlen(aTestString3); i++) {
        printf("( %c )", aTestString3[i]);  // char als char
        printf("[ %02X ]", aTestString3[i]); // char in hexadezimal
    }
}

例如,计算 Unicode 字符数或查看字符串中是否包含特定 Unicode 字符(即代码点)的推荐方法是什么?我很确定一定有一些简单的解决方案,因为例如,这些字符经常用于密码中。

以下是测试程序的输出:

123456789
cheese has 6 character
( c )[ 63 ]( h )[ 68 ]( e )[ 65 ]( e )[ 65 ]( s )[ 73 ]( e )[ 65 ]
123456789
Käse has 5 characters
( K )[ 4B ](  )[ FFFFFFC3 ](  )[ FFFFFFA4 ]( s )[ 73 ]( e )[ 65 ]
123456789
λόγος has 10 characters
(  )[ FFFFFFCE ](  )[ FFFFFFBB ](  )[ FFFFFFCF ](  )[ FFFFFF8C ](  )[ FFFFFFCE ](  )[ FFFFFFB3 ](  )[ FFFFFFCE ](  )[ FFFFFFBF ](  )[ FFFFFFCF ](  )[ FFFFFF82 ]
C 字符串 解析 Unicode

评论

0赞 Some programmer dude 7/3/2023
若要获取 Unicode 字符串中的代码点数,需要第三方库。就像 ICU 图书馆一样。
0赞 Andreas Wenzel 7/3/2023
如果将输出翻译成英文,您的代码将更易于理解。
0赞 ᛉᛉᛉ ᛉᛉᛉ 7/3/2023
对不起,“cheese ist 6 Zeichen lang”只是意味着“奶酪有 6 个字符”。我在上面的代码中解决了这个问题。
0赞 n. m. could be an AI 7/3/2023
@Someprogrammerdude 不,你不知道。这是几行普通的 C 代码。
1赞 n. m. could be an AI 7/3/2023
@ᛉᛉᛉᛉᛉᛉ号使用和函数处理宽字符串,并且只计算 s。在你开始处理异国情调的脚本和稀有的特殊角色之前,你不需要比这更复杂的东西。对于德语和希腊语来说,这已经足够了。wchar_twchar_t

答:

5赞 mediocrevegetable1 7/3/2023 #1

在这种情况下,C 的多字节字符串实用程序很有用。例如,使用一种方法来查找字符串中的字符数(尽管我现在刚刚凑在一起的可能是一个非常幼稚的方法)是这样的:mbrlen

#include <stdio.h>
#include <wchar.h>
#include <locale.h>

size_t string_size(const char *s)
{
    mbstate_t state = {0};
    size_t len = 0;
    for (; *s != '\0'; ++len)
    {
        unsigned c_len;
        for (c_len = 1; mbrlen(s+c_len-1, 1, &state) == -2; ++c_len) {}
        s += c_len;
    }
    return len;
}

int main(void)
{
    setlocale(LC_ALL, "en_US.utf8");
    const char *s = "zß水🍌";
    printf("%zu\n", string_size(s));
}

// Output: 4

使用相同的函数,您还可以通过查找字符的长度来提取单个字符。如果您想使用它,还可以在多字节字符和宽字符之间进行转换。mbrlen