🍣

【C 言語】UTF-8 文字列の表現方法

2024/06/17に公開

動作の確認は LLVM 18 である (Zig 0.13.0)。文字列を収納する配列の型は uint8_t (C99) か char8_t (C23) にする。これらを利用できない場合は unsinged char 型にする。unsigned char 型や char 型は処理系によって振る舞いが変わるのでクロスプラットフォームを対象としたプログラムコードでは避ける

test.c
#include <stdio.h>
#include <stdint.h>
#include <uchar.h>

int main(void)
{
    // C99 stdint.h
    uint8_t str[] = "あいうえお";

    // C23 uchar.h
    char8_t str2[] = "かきくけこ";

    // C99 以前
    unsigned char str3[] = "さしすせそ";

    printf("%s %s %s\n", str, str2, str3);

    return 0;
}

文字列はユニコード、バイト列の16進数のエスケープシーケンス、数値で表現できる

#include <stdio.h>
#include <stdint.h>

int main(void)
{
    uint8_t str[] = "あいう";
    uint8_t str2[] = "\u3042\u3044\u3046";
    uint8_t str3[] = "\U00003042\U00003044\U00003046";
    uint8_t str4[] = "\xE3\x81\x82\xE3\x81\x84\xE3\x81\x86";
    uint8_t str5[] = {0xE3, 0x81, 0x82, 0xE3, 0x81, 0x84, 0xE3,
      0x81, 0x86, 0};
    uint8_t str6[] = {'\xE3', '\x81', '\x82', '\xE3', '\x81',
      '\x84', '\xE3', '\x81', '\x86', '\0'};

    uint8_t str7[10];
    str7[0] = 0xE3;
    str7[1] = 0x81;
    str7[2] = 0x82;
    str7[3] = 0xE3;
    str7[4] = 0x81;
    str7[5] = 0x84;
    str7[6] = 0xE3;
    str7[7] = 0x81;
    str7[8] = 0x86;
    str7[9] = 0;

    printf("%s\n%s\n%s\n%s\n%s\n%s\n%s\n", str, str2, str3, str4,
      str5, str6, str7);

    return 0;
}

C++23 の仕様の一部も利用できることも確認した

#include <stdio.h>
#include <stdint.h>

int main(void)
{
    // https://en.cppreference.com/w/cpp/language/escape

    uint8_t str[] = "\u{1F415}\u{1F408}\u{1F407}";
    uint8_t str2[] = "\N{DOG}\N{CAT}\N{RABBIT}";
    printf("%s\n%s\n", str, str2);

    return 0;
}

バイト列の表記は次のコマンドで調べられる

> echo -n あいうえお | hexdump -e '35/1 "%02X " "\n"'
E3 81 82 E3 81 84 E3 81 86 E3 81 88 E3 81 8A
> echo -n あいうえお | od -tx1 -An 
 e3 81 82 e3 81 84 e3 81 86 e3 81 88 e3 81 8a
> echo -n あいうえお | od -tx1 -An | tr [:lower:] [:upper:]
 E3 81 82 E3 81 84 E3 81 86 E3 81 88 E3 81 8A

コードポイントを調べるには uconv もしくは uniname を使う

> echo -n あいうえお | uconv -x Hex/Unicode; echo
U+3042U+3044U+3046U+3048U+304A

> echo -n あいうえお | LINES=35 uniname
character  byte       UTF-32   encoded as     glyph   name
        0          0  003042   E3 81 82       あ      HIRAGANA LETTER A
        1          3  003044   E3 81 84       い      HIRAGANA LETTER I
        2          6  003046   E3 81 86       う      HIRAGANA LETTER U
        3          9  003048   E3 81 88       え      HIRAGANA LETTER E
        4         12  00304A   E3 81 8A       お      HIRAGANA LETTER O

Discussion