【问题标题】:How to communicate with C stdout?如何与 C 标准输出通信?
【发布时间】:2021-12-01 13:43:51
【问题描述】:

我如何知道我的终端希望以哪种方式接收数据?我应该使用fputcfputwc 还是别的什么?

这是我对fputc 的尝试:

// characters.c
#include <ctype.h>
#include <stdio.h>
int main ( void )
{
    for ( int c = 0 ; c <= 65535 ; c ++ )
            if ( isgraph ( c ) ) fputc ( c , stdout ) ;
}

还有fputwc:

// wcharacters.c
#include <ctype.h>
#include <stdio.h>
#include <wcahr.h>
int main ( void )
{
    for ( wchar_t c = 0 ; c <= 65535 ; c ++ )
            if ( isgraph ( c ) ) fputwc ( c , stdout ) ;
}

使用characters.c,输出与终端打印的前几项的ASCII字符一致(!"#等),然后~之后,就是垃圾了。

我得到与wcharacters.c 类似的结果,除了它不是垃圾,而是? 或其他一些ASCII 字符(但绝大多数是? 的)。

我知道同一个终端支持 Unicode 代码点范围 33 到 65535(十进制)中的许多字符表示,因为我可以使用 Python 3 打印其中的许多字符。

我正在使用Trisquel GNU/Linuxgcc (-std=c99) 和“MATE Terminal”。字体是“Monospace”,听起来很一般,但它似乎支持更多超出 ASCII 范围的字符。

我愿意使用更新的 C 标准。同时,我正在做的项目的一个重点是简单,C 标准似乎随着每个后续标准变得越来越复杂(使用 C17 的uchar.h 会更简单吗?)。

使用我正在开发的功能,用户应该能够将任何图形字符(如isgraph)写入stdout 并且这些字符的外观是“正确的”。

附录

回应@chux-reinstate-monica:

isgraph() 可以使用窄字符,不适用于宽字符。

characters.c 更好的测试可能是:

// charactersAll.c
#include <ctype.h>
#include <limits.h>
#include <stdio.h>
int main ( void )
{
    const int cmax = (
        (unsigned) UCHAR_MAX < (unsigned) INT_MAX
            ) ? (int) UCHAR_MAX : INT_MAX ;
    int c = -1 ;
    while ( c < cmax )
    {
        c ++ ;
        if ( isgraph ( c ) ) fputc ( c , stdout ) ;
    }
}

对比字符.c:

  • 循环遍历所有有效的isgraph 参数。

  • 避免溢出:characters.c 可能会尝试增加 c 超过最大值(即,当 int 为 16 位时;确认:@chux-reinstate-monica,“aside”)。

【问题讨论】:

  • The font is "Monospace," 有“后备”,当您默认使用的字体缺少代码点时,会检查另一种字体是否具有该字符。在我的系统上/etc/fonts/conf.d/45-generic.conf60-generic.conf&lt;alias&lt; binding="same"&gt;&lt;family&gt;emoji&lt;/family&lt;prefer&gt;...

标签: c terminal character stdout non-ascii-characters


【解决方案1】:

我如何知道我的终端期望以哪种方式接收数据?

isgraph() 可以使用窄字符,不适用于宽字符。

最好使用 is...() 函数的受限范围,而不是 [0 ... 65535]。

在所有情况下,参数都是int,其值应表示为unsigned char,或应等于宏EOF 的值。如果参数有任何其他值,则行为未定义。 C17dr § 7.4 1

is...() 不适用于该范围之外的值。


另外:wchar_t 是 16 位无符号时,for ( wchar_t c = 0 ; c &lt;= 65535 ; c ++ ) 有无限循环的风险。


GTG。

【讨论】:

    【解决方案2】:

    你不能直接打印unicode点,大多数终端默认只接受UTF8编码的字符串。

    #include <stdio.h>
    #include <stdint.h>
    
    /**
     * Encode a code point using UTF-8
     *
     * @author Ondřej Hruška <ondra@ondrovo.com>
     * @license MIT
     *
     * @param out - output buffer (min 5 characters), will be 0-terminated
     * @param utf - code point 0-0x10FFFF
     * @return number of bytes on success, 0 on failure (also produces U+FFFD, which uses 3 bytes)
     */
    int utf8_encode(char *out, uint32_t utf)
    {
      if (utf <= 0x7F) {
        // Plain ASCII
        out[0] = (char) utf;
        out[1] = 0;
        return 1;
      }
      else if (utf <= 0x07FF) {
        // 2-byte unicode
        out[0] = (char) (((utf >> 6) & 0x1F) | 0xC0);
        out[1] = (char) (((utf >> 0) & 0x3F) | 0x80);
        out[2] = 0;
        return 2;
      }
      else if (utf <= 0xFFFF) {
        // 3-byte unicode
        out[0] = (char) (((utf >> 12) & 0x0F) | 0xE0);
        out[1] = (char) (((utf >>  6) & 0x3F) | 0x80);
        out[2] = (char) (((utf >>  0) & 0x3F) | 0x80);
        out[3] = 0;
        return 3;
      }
      else if (utf <= 0x10FFFF) {
        // 4-byte unicode
        out[0] = (char) (((utf >> 18) & 0x07) | 0xF0);
        out[1] = (char) (((utf >> 12) & 0x3F) | 0x80);
        out[2] = (char) (((utf >>  6) & 0x3F) | 0x80);
        out[3] = (char) (((utf >>  0) & 0x3F) | 0x80);
        out[4] = 0;
        return 4;
      }
      else {
        // error - use replacement character
        out[0] = (char) 0xEF;
        out[1] = (char) 0xBF;
        out[2] = (char) 0xBD;
        out[3] = 0;
        return 0;
      }
    }
    
    int main ( void )
    {
        char out[6];
        for ( uint32_t c = 0 ; c <= 65535 ; c ++ ) {
            utf8_encode(out, c);
            printf("%s", out);
        }
    }
    

    【讨论】:

    • 注意:c == 0printf("%s", out); 什么也不打印。
    【解决方案3】:

    如何与C标准输出通信?

    这是非常定义的实现。

    我正在使用... GNU/Linux

    要与 Linux terminal driver 通信,请使用 ioctlstermios.h 库。要与真实终端通信,您可以将输出设置为原始并发送special escape sequence,然后您会得到标准输入的答案。例如ESC[6n request cursor position 报告为ESC[#;#R)

    我如何知道我的终端期望以哪种方式接收数据?

    来自语言环境。例如LC_ALL=en_US.UTF-8 表示它想要UTF-8LC_ALL=tr_TR.ISO-8859-9 表示它想要ISO-8859-9 编码中的字符。

    注意来自cppreference setlocale:

    在程序启动期间,相当于 setlocale(LC_ALL, "C");在运行任何用户代码之前执行。

    通常在main 之后执行setlocale(LC_ALL, "") 以使用终端的语言环境。

    我应该使用 fputc、fputwc 还是其他什么?

    没关系,但您应该使用其中的一个,一旦切换流,就很难将其切换回来。使用fputc,您可以将multibyte charactersfputwc 输出相同的宽字符,但可以肯定的是,可能存在宽字符没有多字节字符的情况。在具有 UTF-8 语言环境的 Linux 上,使用 fputc 您可以输出与使用 UTF-32 的 fputwc 相同的 UTF-8 代码点。 GLIBC Extended char intro 是一本好书。

    当您使用fputwc 时,在幕后glibc 将UTF-32 转换为特定于语言环境的编码,大约在behind here 初始化here 的地方。它基本上为每个字符调用iconv

    ~之后就是垃圾了。

    isgraph ( c ) 对于大于UCHAR_MAXcc 无效,而c 不同于EOF#include &lt;wcahr.h&gt; 无效。

    我有语言环境LC_CTYPE=pl_PL.UTF-8。以下程序:

    #include <ctype.h>
    #include <locale.h>
    #include <stdio.h>
    #include <wchar.h>
    #include <wctype.h>
    int main() {
        setlocale(LC_ALL, "");
        for (wchar_t c = 63000; c <= 65535; c++)
            if (iswgraph(c))
                fputwc(c, stdout);
    }
    

    会输出一些图形字符:

    ...
    

    如前所述,如果您愿意,也可以对 fputc 执行相同操作:

    #include <ctype.h>
    #include <locale.h>
    #include <stdio.h>
    #include <wchar.h>
    #include <wctype.h>
    #include <stdlib.h>
    #include <limits.h>
    int main() {
        setlocale(LC_ALL, "");
        for (wchar_t c = 63000; c <= 65535; c++) {
            if (iswgraph(c)) {
                char s[MB_LEN_MAX];
                wctomb(s, c);
                for (char *p = s; *p; ++p) {
                    fputc(*p, stdout);
                }
            }
        }
    }
    

    除了垃圾,它是 ?'s 或 som

    您的程序正在使用C 语言环境。 C 语言环境不能代表 ASCII 以外的任何内容。

    使用 C17 的 uchar.h 会更简单吗?

    使用wchar_t。这取决于您的具体应用。在通常情况下,让我们相信 C 标准、C 实现和操作系统,并使用 wchar_t 作为“终极”字符。

    【讨论】:

      猜你喜欢
      • 2014-07-31
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多