【问题标题】:ANSI C: isprint() returns true for non-ASCII character?ANSI C:isprint() 为非 ASCII 字符返回 true?
【发布时间】:2012-02-18 21:38:27
【问题描述】:

我有一些 C 代码应该打印文件的全部内容。该程序以前打印文件很好,但是当它打印一秒钟时,我一直看到一个绝对不应该存在的 Unicode 字符。

int c = fgetc(file);
putchar((!isprint(c) ? : c));

(包裹在while(!feof(file))中)
除非我弄错了,否则应该只打印 ASCII 可打印字符。无论如何,它打印的第一件事是\357\277\275,它不是 ASCII,也不能打印。

该文件仅包含以下内容:foo+bar.foo+t-bar.foo+completely fake

它会打印出这个:�foo+bar.foo+t-bar.foo+completely fake(在奇怪的字符和其他字符之间有一个换行符)。

只需将其全部打印出来(如putchar(c))将完全相同的字符放在行尾。

我什至尝试过使用另一个文件(通过重命名旧文件并使用指向另一个文件的软链接),但我得到了完全相同的结果。

如果文件为空,它也会这样做。

该文件是完全纯文本,用 vim 创建的,没有什么特别之处。

这是原始代码:

#include <stdio.h>
#include <stdlib.h>
#include <ctype.h>
#include <errno.h>

int main(void)
{
    char *headp = "../include/header";
    char *listp = "../.piclist";
    FILE *head, *list;

    puts("Content-Type: text/html; charset=utf-8\nExpires: 0\n");

    puts("<!DOCTYPE html>\n<html lang='en'>\n<head>");
    puts("\t<title>Foo</title>");
    puts("\t<link rel='stylesheet' href='/css/main.css' />");
    puts("\t<link rel='stylesheet' href='/css/foo.css' />");
    puts("</head>\n<body>");

    head = fopen(headp, "r");
    if (errno) {
            perror("cannot open include/header");
            errno = 0;
    } else {
            while (!feof(head)) putchar(fgetc(head));
            putchar('\n');
    fclose(head);
    }

    list = fopen(listp, "r");
    if (errno) perror("cannot open .piclist");
    else {
    while (!feof(list)) {
            while (!feof(list)) {
                    int c = fgetc(list);
                    putchar((!isprint(c) ? : c));
            }
    }
    fclose(list);
    } /* else */

    fputs("\n<footer>\n\t<hr />\n\t<p>Copyright 2011-2012 the ", stdout);
    fputs("<a href='mailto:foo@example.com'> ", stdout);
    fputs("Foo Bar of Baz</a> of ", stdout);
    fputs("<a href='http://blah.org'>Blah United ", stdout);
    fputs("</a></p>\n</footer>\n</body>\n</html>\n",stdout);

    return 0;
}

【问题讨论】:

  • ... 也是当文件为空时,因为你的 feof() 使用是错误的。请贴出真实代码。
  • 您能否展示一个完整的、可编译的示例来演示该问题?
  • putchar((!isprint(c) ? : c));
  • @H2C03 这是一个 GNU 扩展 gcc.gnu.org/onlinedocs/gcc/Conditionals.html
  • “奇怪的字符”是U+FFFD,UTF-8编码。

标签: c unicode ascii non-ascii-characters ansi-c


【解决方案1】:

不要使用 feof()(至少不是你使用它的方式)

另外,您对 errno 的使用是错误的。 errno only 在某些操作指示错误返回后包含一个可用值(主要以 NULL 或 -1 作为返回值)

#include <stdio.h>
#include <stdlib.h>
#include <ctype.h>
#include <errno.h>

int main(void)
{
    char *headp = "../include/header";
    char *listp = "../.piclist";
    FILE *head, *list;
    int ch;

    puts("Content-Type: text/html; charset=utf-8\nExpires: 0\n");

    puts("<!DOCTYPE html>\n<html lang='en'>\n<head>");
    puts("\t<title>Warrenton Latin School | Gallery</title>");
    puts("\t<link rel='stylesheet' href='/css/main.css' />");
    puts("\t<link rel='stylesheet' href='/css/gallery.css' />");
    puts("</head>\n<body>");

    head = fopen(headp, "r");
    if (!head) {
            perror("cannot open include/header");
            errno = 0;
    } else {
        while (1) {
            ch = fgetc(head);
            if (ch == EOF) break;
            putchar(ch);
            }
        putchar('\n');
        fclose(head);
    }

    list = fopen(listp, "r");
    if (!list) perror("cannot open .piclist");
    else while (1) {
        ch = fgetc(list);
        if (ch == EOF) break;
        putchar((!isprint(c) ? : c));
    }
    fclose(list);

    fputs("\n<footer>\n\t<hr />\n\t<p>Copyright 2011-2012 the ", stdout);
    fputs("<a href='mailto:warrentonlatinschool@gmail.com'> ", stdout);
    fputs("Warrenton Latin School</a> co-op of ", stdout);
    fputs("<a href='http://warrentonumc.org'>Warrenton United ", stdout);
    fputs("Methodist Church</a></p>\n</footer>\n</body>\n</html>\n",stdout);

    return 0;
}

【讨论】:

  • 好吧,我对 errno 和 perror 的使用已经奏效了;虽然我只在文件不存在时测试过。
  • feof() 返回错误发生后。在这种情况下:读取最后一个字符后, fgetc() 在每次后续调用时返回 EOF。您“使用”第一个 EOF(并认为它是一个有效字符,并打印它(它可能会在输出文件中变为 0xff))只有在那之后,feof() 才返回不为零。
  • 我清理了代码,不再像那样使用 feof(),现在我的问题肯定已经解决了。谢谢。
  • 我无意解决您的问题。我只修复了明显的缺陷;-) 请注意:if (ch == EOF) break; 不是很优雅(实际上很粗鲁),但它是用于教育目的。 (为了避免混淆 while ((ch=fgetc(fp)) != EOF){} 这或多或少是标准的成语,但可能会让初学者有点困惑。
【解决方案2】:

忽略代码中可能出现的错误isprint() 认为所有字符都可打印,0x00 - 0x1f0x7f 除外。

仍然可以打印 UTF BOM 和 7 位 ANSI 之外的其他字符(尽管它们的含义可能会根据编码而改变)。

【讨论】:

  • 嗯。虽然使用if (c &lt; 127 &amp;&amp; c &gt; 31) putchar(c); 具有完全相同的效果。
  • 不,127 以上的字符仍然可以打印。
  • 从您的评论中截取的内容不应打印 unicode 字符。
【解决方案3】:

当你把 ?: 的第二个操作符留空时,它等于条件的结果。对于不可打印的字符,isprintc(c) 返回 0,因此三元运算符的条件部分为 !0,等于 1。因此 putchar 尝试打印无效的 ASCII 字符并中断。

【讨论】:

  • 谢谢。在那里有一个空字符串常量是否合法,或者我应该为此做些什么?
  • 将条件移到外面:if(!condition) putchar(...); 你不能定义一个空字符('')和\0(或空字符串的第一个元素;注意数据类型)会输出\0.
猜你喜欢
  • 2021-03-08
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-07-14
  • 2021-12-31
  • 1970-01-01
  • 1970-01-01
  • 2010-12-30
相关资源
最近更新 更多