ANSI C: isprint() возвращает true для символа, отличного от ASCII?

У меня есть код 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:[email protected]'> ", 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;
}

person Aidan Medcalf    schedule 25.01.2012    source источник
comment
... также когда файл пуст, потому что вы неправильно используете feof(). Пожалуйста, опубликуйте реальный код.   -  person wildplasser    schedule 25.01.2012
comment
Не могли бы вы показать полный компилируемый пример, демонстрирующий проблему?   -  person NPE    schedule 25.01.2012
comment
putchar((!isprint(c) ? : c)); ‹- как это вообще компилируется?   -  person    schedule 25.01.2012
comment
Странный символ — U+FFFD, закодированный в UTF-8.   -  person dan04    schedule 25.01.2012
comment
Какой формат у файла? Какой формат дисплея? Использование многобайтовой строки (UTF-8, UTF-16) в качестве однобайтовой или наоборот является наиболее распространенным источником ошибок, подобных вашей. И да, весь этот хлам наверняка можно распечатать (печатаемый === не управляющий символ). Используйте шестнадцатеричный редактор для исследования файла, он выглядит как спецификация. Большинство текстовых редакторов обработают спецификацию и представят вам файл как пустой, в то время как на самом деле перед ним добавлено 2-3 байта. (знак порядка байтов).   -  person Agent_L    schedule 25.01.2012


Ответы (3)


НЕ ИСПОЛЬЗУЙТЕ feof() (по крайней мере, не так, как вы его используете)

Кроме того, вы неправильно используете errno. errno только содержит полезное значение после того, как какая-либо операция указала на возврат ошибки (в основном 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:[email protected]'> ", 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;
}
person wildplasser    schedule 25.01.2012
comment
Ну, мое использование errno и perror сработало; хотя я проверял это только тогда, когда файлов не было. - person Aidan Medcalf; 25.01.2012
comment
feof() возвращает ошибку после того, как она произошла. В этом случае: после прочтения последнего символа fgetc() возвращает EOF при каждом последующем вызове. Вы потребляете первый EOF (и считаете его допустимым символом, и печатаете его (в выходном файле он, вероятно, станет 0xff)) И только после этого feof() возвращает не ноль. - person wildplasser; 25.01.2012
comment
Я почистил код, чтобы не использовать feof() таким образом, и теперь моя проблема определенно решена. Спасибо. - person Aidan Medcalf; 25.01.2012
comment
У меня не было цели решить вашу проблему. Исправил только очевидные недочеты ;-) Обратите внимание: if (ch == EOF) break; не очень изящно (на самом деле довольно грубо), но предназначено для воспитательных целей. (чтобы избежать запутанной while ((ch=fgetc(fp)) != EOF){}, которая является более или менее стандартной идиомой, но может немного сбить с толку новичка. - person wildplasser; 26.01.2012

Игнорируя возможные ошибки в вашем коде, isprint() считает все символы печатаемыми, кроме 0x00 - 0x1f и 0x7f.

Такие вещи, как UTF BOM и другие символы за пределами 7-битного ANSI, все еще могут быть напечатаны (несмотря на то, что их значение может меняться в зависимости от кодировки).

person Mario    schedule 25.01.2012
comment
Хм. Хотя использование if (c < 127 && c > 31) putchar(c); имело точно такой же эффект. - person Aidan Medcalf; 25.01.2012
comment
Нет, символы выше 127 по-прежнему считаются печатаемыми. - person Mario; 25.01.2012
comment
С вырезанным из вашего комментария он не должен печатать символы Юникода. - person Mario; 25.01.2012

Когда вы оставляете 2-й оператор ?: пустым, он равен результату условного оператора. Для непечатаемых символов isprintc(c) возвращает 0, поэтому условной частью троичного оператора является !0, что равно 1. Поэтому putchar пытается напечатать недопустимый символ ASCII и прерывается.

person xpapad    schedule 25.01.2012
comment
Спасибо. Будет ли законно иметь пустую строковую константу, или есть что-то еще, что я должен сделать для этого эффекта? - person Aidan Medcalf; 25.01.2012
comment
Переместите условное выражение наружу: if(!condition) putchar(...); Вы не можете определить пустой символ (''), а \0 (или первый элемент пустой строки; обратите внимание на типы данных) выведет \0. - person Mario; 25.01.2012