首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >问答首页 >如何迭代unicode字符,并在屏幕上使用printf在C中打印它们?

如何迭代unicode字符,并在屏幕上使用printf在C中打印它们?
EN

Stack Overflow用户
提问于 2014-07-30 09:37:01
回答 4查看 6.7K关注 0票数 5

我想遍历所有(至少是16位) unicode字符,然后用C在屏幕上打印它们。

我知道这方面有一些相关的问题,但它们并不能解决C语言中的printf问题,但如果有可能的话,这正是我想要实现的。我想这应该是可能的,也许是用一个我不知道的诡计。

由于我想使用printf,所以我想到了这样的事情:

代码语言:javascript
运行
复制
for (int i = 0x0000; i <= 0xffff; i++) {

    //then somehow increment the string
    char str[] = "\u25A1\n";
    printf("%s", str);

    char str[] = "\u25A2\n";
    printf("%s", str);

    char str[] = "\u25A3\n";
    printf("%s", str);

    ...

}

但是增加unicode代码点有点问题,这里是\u25A1。我知道这本身是不可能的,因为像\u0000这样的字符是不可打印的,编译器拒绝。但是除此之外,我如何从十六进制增加到用printf打印字符。

EN

回答 4

Stack Overflow用户

回答已采纳

发布于 2014-07-30 11:26:36

如果定义了__STDC_ISO_10646__宏,则宽字符对应于Unicode编码点。因此,假设一个能够表示您感兴趣的字符的区域设置,您只需通过printf()格式转换实现%lc宽字符:

代码语言:javascript
运行
复制
#include <stdio.h>
#include <locale.h>

#ifndef __STDC_ISO_10646__
#error "Oops, our wide chars are not Unicode codepoints, sorry!"
#endif
int main()
{
        int i;
        setlocale(LC_ALL, "");

        for (i = 0; i < 0xffff; i++) {
                printf("%x - %lc\n", i, i);
        }

        return 0;
}
票数 6
EN

Stack Overflow用户

发布于 2014-07-30 12:43:45

在C99中,可以使用宽字符到多字节字符转换函数wctomb()wcrtomb(),使用当前字符集将每个代码点转换为本地表示形式。(代码点位于当前字符集中,而不是Unicode中。)记住要使用setlocale()来确保转换函数知道用户区域设置(最重要的是,使用的当前字符集)。转换函数使用LC_CTYPE类别,但是您仍然应该使用setlocale(LC_ALL, "");,就像对任何其他区域设置识别程序一样。

(并不是所有的系统都安装了C.UTF-8语言环境,所以我不建议使用setlocale(LC_ALL, "C.UTF-8");使用UTF-8将语言环境覆盖到标准C。它适用于某些系统,但并不是全部。例如,AFAIK在基于Fedora的Linux发行版中不起作用。

由于您希望输出所有Unicode代码点,我建议采用一种不同的方法:使用一种通用字符集转换格式,即UTF-8、UTF-16 (UCS-2在1996年被UTF-16取代)或UTF-32 (也称为UCS-4)。UTF-8是网络上最常用的一种--尤其是在你现在看到的这个网页上--而且很容易使用。

有关为什么应该更喜欢UTF-8而不是“本机宽字符串”的进一步阅读,请参见utf8everywhere.org

如果您想要真正可移植的代码,可以使用这个头文件utf8.h将UTF-8转换为unicode代码点(utf8_to_code()),将Unicode代码点转换为UTF-8 (code_to_utf8()):

代码语言:javascript
运行
复制
#ifndef   UTF8_H
#define   UTF8_H
#include <stdlib.h>
#include <errno.h>

#define   UTF8_MAXLEN 6

static size_t utf8_to_code(const unsigned char *const buffer, unsigned int *const codeptr)
{
    if (!buffer) {
        errno = EINVAL;
        return 0;
    }

    if (*buffer == 0U) {
        errno = 0;
        return 0;
    }

    if (*buffer < 128U) {
        if (codeptr)
            *codeptr = buffer[0];
        return 1;
    }

    if (*buffer < 192U) {
        errno = EILSEQ;
        return 0;
    }

    if (*buffer < 224U) {
        if (buffer[1] >= 128U && buffer[1] < 192U)
            return ((buffer[0] - 192U) << 6U)
                 |  (buffer[1] - 128U);
        errno = EILSEQ;
        return 0;
    }

    if (*buffer < 240U) {
        if (buffer[1] >= 128U && buffer[1] < 192U &&
            buffer[2] >= 128U && buffer[2] < 192U)
            return ((buffer[0] - 224U) << 12U)
                 | ((buffer[1] - 128U) << 6U)
                 |  (buffer[2] - 128U);
        errno = EILSEQ;
        return 0;
    }

    if (*buffer < 248U) {
        if (buffer[1] >= 128U && buffer[1] < 192U &&
            buffer[2] >= 128U && buffer[2] < 192U &&
            buffer[3] >= 128U && buffer[3] < 192U)
            return ((buffer[0] - 240U) << 18U)
                 | ((buffer[1] - 128U) << 12U)
                 | ((buffer[2] - 128U) << 6U)
                 |  (buffer[3] - 128U);
        errno = EILSEQ;
        return 0;
    }

    if (*buffer < 252U) {
        if (buffer[1] >= 128U && buffer[1] < 192U &&
            buffer[2] >= 128U && buffer[2] < 192U &&
            buffer[3] >= 128U && buffer[3] < 192U &&
            buffer[4] >= 128U && buffer[4] < 192U)
            return ((buffer[0] - 248U) << 24U)
                 | ((buffer[1] - 128U) << 18U)
                 | ((buffer[2] - 128U) << 12U)
                 | ((buffer[3] - 128U) << 6U)
                 |  (buffer[4] - 128U);
        errno = EILSEQ;
        return 0;
    }

    if (*buffer < 254U) {
        if (buffer[1] >= 128U && buffer[1] < 192U &&
            buffer[2] >= 128U && buffer[2] < 192U &&
            buffer[3] >= 128U && buffer[3] < 192U &&
            buffer[4] >= 128U && buffer[4] < 192U &&
            buffer[5] >= 128U && buffer[5] < 192U)
            return ((buffer[0] - 252U) << 30U)
                 | ((buffer[1] - 128U) << 24U)
                 | ((buffer[2] - 128U) << 18U)
                 | ((buffer[3] - 128U) << 12U)
                 | ((buffer[4] - 128U) << 6U)
                 |  (buffer[5] - 128U);
        errno = EILSEQ;
        return 0;
    }

    errno = EILSEQ;
    return 0;
}

static size_t code_to_utf8(unsigned char *const buffer, const unsigned int code)
{
    if (code < 128U) {
        buffer[0] = code;
        return 1;
    }
    if (code < 2048U) {
        buffer[0] = 0xC0U | (code >> 6U);
        buffer[1] = 0x80U | (code & 0x3FU);
        return 2;
    }
    if (code < 65536) {
        buffer[0] = 0xE0U | (code >> 12U);
        buffer[1] = 0x80U | ((code >> 6U) & 0x3FU);
        buffer[2] = 0x80U | (code & 0x3FU);
        return 3;
    }
    if (code < 2097152U) {
        buffer[0] = 0xF0U | (code >> 18U);
        buffer[1] = 0x80U | ((code >> 12U) & 0x3FU);
        buffer[2] = 0x80U | ((code >> 6U) & 0x3FU);
        buffer[3] = 0x80U | (code & 0x3FU);
        return 4;
    }
    if (code < 67108864U) {
        buffer[0] = 0xF8U | (code >> 24U);
        buffer[1] = 0x80U | ((code >> 18U) & 0x3FU);
        buffer[2] = 0x80U | ((code >> 12U) & 0x3FU);
        buffer[3] = 0x80U | ((code >> 6U) & 0x3FU);
        buffer[4] = 0x80U | (code & 0x3FU);
        return 5;
    }
    if (code <= 2147483647U) {
        buffer[0] = 0xFCU | (code >> 30U);
        buffer[1] = 0x80U | ((code >> 24U) & 0x3FU);
        buffer[2] = 0x80U | ((code >> 18U) & 0x3FU);
        buffer[3] = 0x80U | ((code >> 12U) & 0x3FU);
        buffer[4] = 0x80U | ((code >> 6U) & 0x3FU);
        buffer[5] = 0x80U | (code & 0x3FU);
        return 6;
    }
    errno = EINVAL;
    return 0;
}

#endif /* UTF8_H */

它不是快速的,但它应该容易理解,并支持所有可能的Unicode代码点(U+0000到U+10FFFF,包括在内),在所有系统上,至少32位无符号ints。在具有16位无符号ints的系统上,编译器可能会警告无法访问的代码,并且它只支持前65536个代码点(U+0000到U+FFFF)。

使用上面的utf8.h,您可以轻松地编写一个C程序,该程序输出包含您需要的Unicode字符(不包括控制字符U+0000-U+001 F和U+007F-U+00BF,包含无效代码点U+D800-U+DFFF )。例如,page.c

代码语言:javascript
运行
复制
#include <stdlib.h>
#include <stdio.h>
#include <errno.h>
#include "utf8.h"

int main(void)
{
    unsigned char  ch[UTF8_MAXLEN + 1];
    unsigned int   i;
    const char    *str;
    size_t         n, len;

    /* HTML5 DOCTYPE */
    printf("<!DOCTYPE html>\n");
    printf("<html>\n");

    /* Header part. */
    printf(" <head>\n");
    printf("  <title> Unicode character list </title>\n");
    printf("  <meta http-equiv=\"Content-Type\" content=\"text/html; charset=UTF-8\">\n");
    printf("  <style type=\"text/css\">\n");
    /* with internal CSS stylesheet: */
    printf("   html {\n");
    printf("    font-family: \"DejaVu Mono\", \"Courier New\", \"Courier\", monospace;\n");
    printf("    font-weight: normal;\n");
    printf("    font-size: 100%%;\n");
    printf("    text-decoration: none;\n");
    printf("    background: #f7f7f7;\n");
    printf("    color: #000000;\n");
    printf("    padding: 0 0 0 0;\n");
    printf("    border: 0 none;\n");
    printf("    margin: 0 0 0 0\n");
    printf("   }\n");

    printf("   body {\n");
    printf("    background: #ffffff;\n");
    printf("    padding: 0.5em 1em 0.5em 1em;\n");
    printf("    border: 1px solid #cccccc;\n");
    printf("    margin: 0 auto auto auto;\n");
    printf("    width: 12em;\n");
    printf("    text-align: center;\n");
    printf("   }\n");

    printf("   p {\n");
    printf("    padding: 0 0 0 0;\n");
    printf("    border: 0 none;\n");
    printf("    margin: 0 0 0 0;\n");
    printf("    outline: 0 none;\n");
    printf("    text-align: center;\n");
    printf("   }\n");

    printf("   p.odd {\n");
    printf("    background: #efefef;\n");
    printf("   }\n");

    printf("   p.even {\n");
    printf("    background: #f7f7f7;\n");
    printf("   }\n");

    printf("   span.code {\n");
    printf("    width: 8em;\n");
    printf("    text-align: right;\n");
    printf("   }\n");

    printf("   span.char {\n");
    printf("    width: 4em;\n");
    printf("    text-align: left;\n");
    printf("   }\n");

    printf("  </style>\n");
    printf(" </head>\n");

    /* Body part. */
    printf(" <body>\n");

    n = 0;
    for (i = 0U; i <= 0xFFFFU; i++) {

        /* Skip Unicode control characters. */
        if ((i >= 0U && i <= 31U) ||
            (i >= 127U && i <= 159U))
            continue;

        /* Skip invalid Unicode code points. */
        if (i >= 0xD800U && i <= 0xDFFFU)
            continue;

        len = code_to_utf8(ch, i);
        if (len > 0) {
            ch[len] = '\0';

            /* HTML does not like " & < > */
            if (i == 32U)
                str = "&nbsp;";
            else
            if (i == 34U)
                str = "&#34;";
            else
            if (i == 38U)
                str = "&amp;";
            else
            if (i == 60U)
                str = "&lt;";
            else
            if (i == 62U)
                str = "&gt;";
            else
                str = (const char *)ch;

            if (n & 1) {
            printf("  <p class=\"odd\" title=\"%u in decimal, &amp;#%u; = %s\">", i, i, str);
                printf("<span class=\"code\">U+%04X</span>", i);
                printf(" <span class=\"char\">%s</span>", str);
                printf("</p>\n");
            } else {
                printf("  <p class=\"even\" title=\"%u in decimal, &amp;#%u; = %s\">", i, i, str);
                printf("<span class=\"code\">U+%04X</span>", i);
                printf(" <span class=\"char\">%s</span>", str);
                printf("</p>\n");
            }

            n++;
        }
    }

    printf(" </body>\n");
    printf("</html>\n");

    return EXIT_SUCCESS;
}

将输出重定向到文件,您可以在任意浏览器中打开该文件。如果您的浏览器是正常的,并且不处理与从web服务器获得的本地文件有任何不同的本地文件,那么您应该看到正确的输出。

(如果您在U+00A0之后看到每个代码点有多个字符,您的浏览器已经决定因为该文件是本地的,所以它使用的是它显式声明使用的不同的字符集。如果发生这种情况,请切换到正常的浏览器,或者重写字符集选择。)

如果需要,只需将代码打印为UTF-8文本,例如使用text.c

代码语言:javascript
运行
复制
#include <stdlib.h>
#include <stdio.h>
#include <errno.h>
#include "utf8.h"

int main(void)
{
    unsigned char  ch[UTF8_MAXLEN + 1];
    unsigned int   i;
    size_t         len;

    for (i = 0U; i <= 0xFFFFU; i++) {

        /* Skip Unicode control characters. */
        if ((i >= 0U && i <= 31U) ||
            (i >= 127U && i <= 159U))
            continue;

        /* Skip invalid Unicode code points. */
        if (i >= 0xD800U && i <= 0xDFFFU)
            continue;

        len = code_to_utf8(ch, i);
        if (len > 0) {
            ch[len] = '\0';
            printf("U+%04X %s \n", i, ch);
        }
    }

    return EXIT_SUCCESS;
}

但是,您必须确保终端或终端模拟器支持UTF-8并使用UTF-8区域设置,或者您将输出重定向到文本文件并在编辑器中打开该文件,该编辑器假设文件使用UTF-8或允许显式选择UTF-8字符集。

注意,每个字符前后都有一个空格。因为有些代码点是组合字符,所以它们可能根本不会出现,除非它们可以与另一个字符组合,而且大多数(全部?)和空间结合得很好。

如果你使用Windows,那么你必须遵从微软的愚蠢,并在输出的开头添加一个特殊的“字节顺序标记”( printf("\xEF\xBB\xBF"); ),这样它的实用程序(如记事本)就可以识别出文件为UTF-8。这是一个只有Windows的疣,并以这样的方式对待。

有问题吗?

票数 5
EN

Stack Overflow用户

发布于 2014-07-30 09:41:06

将16位Unicode编码点转换为多字节字符序列的函数是c16rtomb;如果要处理32位代码点,也可以使用c32rtomb

代码语言:javascript
运行
复制
#include <uchar.h>

mbstate_t ps;
char buf[MB_CUR_MAX];
size_t bytes = c16rtomb(buf, i, &ps);
if (bytes != (size_t) -1) {
  printf("%.*s\n", bytes, buf);
}

如果c16rtomb不可用,您将需要使用特定于平台的设施。

票数 2
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/25033430

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档