C fopen и fgets возвращают странные символы вместо содержимого файла
Я делаю упражнение по кодированию, и мне нужно открыть файл данных, который содержит много данных. Это файл.raw. Перед созданием приложения я открываю файл "card.raw" в текстовом редакторе и в гекседиторе. Если вы откроете его в текстовом редакторе, в первой строке вы увидите "bit.ly/18gECvy ˇÿˇ‡JFIFHHˇ€Cˇ€Cˇ¿Vˇƒ". (URL указывает на Рика Ролла как шутку профессора.)
Поэтому я начинаю создавать свое приложение, чтобы открыть тот же файл "card.raw". Я делаю первоначальные проверки, чтобы увидеть, как приложение выводит на консоль те же "вещи", что и при открытии его с помощью TextEdit. Вместо распечатки я вижу, когда открываю его с помощью TextEdit (см. Текст выше), он начинает и продолжает распечатывать текст, который выглядит следующим образом:
\ 377 \ 304 'u \ 204 \ 206 \ 226 \ 262 \ 302 \ 3227 \ 205 \ 246 \ 266 \ 342GSc \ 224 \ 225 \ 245 \ 265 \ 305 \ 306 \ 325 \ 326Wgs \ 244 \ 346 (ш \ 345 \ 362 \ 366 \ 207 \ 264 \ 304! \ 223 \ 227 \ \2678H 247 \ 250 \ 343 \ 344 \ 365 \ 377 \ 304
Теперь я понятия не имею, как называются '\' и цифры (что я ищу, чтобы прочитать больше?), Почему он печатает, а не символы (юникод?), Которые я вижу, когда открываю в TextEdit, или если я могу преобразовать этот вывод в шестнадцатеричный или Unicode.
Мой код:
#include <stdio.h>
#include <string.h>
#include <limits.h>
int main(int argc, const char * argv[]) {
FILE* file;
file = fopen("/Users/jamesgoldstein/CS50/CS50Week4/CS50Recovery/CS50Recovery/CS50Recovery/card.raw", "r");
char output[LINE_MAX];
if (file != NULL)
{
for (int i = 1; fgets(output, LINE_MAX, file) != NULL; i++)
{
printf("%s\n", output);
}
}
fclose(file);
return 0;
}
ОБНОВЛЕННЫЙ И ПРОСТОЙ КОД С ИСПОЛЬЗОВАНИЕМ fread()
#include <stdio.h>
#include <string.h>
int main(int argc, const char * argv[]) {
FILE* fp = fopen("/Users/jamesgoldstein/CS50/CS50Week4/CS50Recovery/CS50Recovery/CS50Recovery/card.raw", "rb");
char output[256];
if (fp == NULL)
{
printf("Bad input\n");
return 1;
}
for (int i = 1; fread(output, sizeof(output), 1, fp) != NULL; i++)
{
printf("%s\n", output);
}
fclose(fp);
return 0;
}
Вывод частично правильный (вот фрагмент начала):
bit.ly/18gECvy
\ 377 \ 330 \ 377 \ 340 \ 221 \ 241 \ 26145 \ 301 \ 321 \ 341 "# & 23DE \ 3616BFRTUe \ 202CVbdfrtv \ 222 \ 242 'u \ 204 \ 206 \ 226 \ 262 \ 302 \ 3227 \ 205 \ 246 \ 266 \ 342GSc \ 224 \ 225 \ 245 \ 265 \ 305 \ 306 \ 325 \ 326Wgs \ 244 \ 346 (ш \ 345 \ 362 \ 366 \ 207 \ 264 \ 304! \223\227\2678H\247\250\343\344\365\377\304 =\311\345\264\352\354 7\222\315\306\324+\342\364\273\274\205$z\262\313g-\343wl\306\375My:}\242o\210\377 3(\266l\356\307T 2 "2\377 \267\212ǑP\2218 \344
Фактический фрагмент файла card.raw начала
bit.ly/18gECvy ˇÿˇ‡JFIFHHˇ€Cˇ€Cˇ¿Vˇƒ
ƒÖ
! 1AQa $% qÅë ° ± 45¡— · "# & 23DEÒ6BFRTUeÇCVbdfrtví ¢
3 ответа
Я думаю, что вы должны открыть.raw файл в режиме "rb"
, Тогда используйте fread()
Из наличия строки "JFIF"
в первой строке файла card.raw
("bit.ly/18gECvy ˇÿˇ‡JFIFHHˇ€Cˇ€Cˇ¿Vˇƒ") похоже card.raw
является файлом формата изображения JPEG, в который вставлен URL bit.ly в начале.
В этом случае вы увидите странные / специальные символы, потому что это совсем не обычный текстовый файл.
Кроме того, как указал Давмак, то, как вы используете fgets
не подходит, даже если вы имели дело с реальным текстовым файлом. При работе с простыми текстовыми файлами в C лучший способ - прочитать весь файл за раз, а не построчно, при условии, что доступно достаточно памяти:
size_t f_len, f_actualread;
char *buffer = NULL;
fseek(file, 0, SEEK_END)
f_len = ftell(fp);
rewind(fp);
buffer = malloc(f_len + 1);
if(buffer == NULL)
{
puts("malloc failed");
return;
}
f_actualread = fread(buffer, 1, f_len, file);
buffer[f_actualread] = 0;
printf("%s\n", output);
free(buffer);
buffer = NULL;
Таким образом, вам не нужно беспокоиться о длине строки или о чем-то подобном.
Вы, вероятно, должны использовать fread
скорее, чем fgets
, поскольку последний действительно предназначен для чтения текстовых файлов, и это явно не текстовый файл.
Ваш обновленный код на самом деле имеет ту самую проблему, о которой я изначально писал (но с тех пор отозвал), так как вы сейчас используете fread
скорее, чем fgets
:
for (int i = 1; fread(output, sizeof(output), 1, fp) != NULL; i++)
{
printf("%s\n", output);
}
Т.е. вы печатаете output
буфер, как если бы это была строка с нулевым символом в конце, хотя на самом деле это не так. Лучше использовать fwrite
в STDOUT
,
Тем не менее, я думаю, что суть проблемы здесь заключается в попытке отобразить произвольные байты (которые на самом деле не представляют символьную строку) для терминала. Терминал может интерпретировать некоторые последовательности байтов как команды, которые влияют на то, что вы видите. Также, textEdit
может определить, что файл находится в некоторой кодировке символов и декодировать символы соответственно.
Теперь я понятия не имею, как называются "\" и цифры (что мне искать, чтобы узнать больше?)
Они похожи на восьмеричные побеги для меня.
почему он печатает это вместо символов (юникод?)
Это не имеет ничего общего с юникодом. Может быть, это ваш эмулятор терминала, который решает, что эти символы недоступны для печати, и заменяет их escape-последовательностью.
Короче говоря, я думаю, что ваш метод (визуально сравнивающий то, что вы видите в текстовом редакторе с тем, что вы видите в терминале) имеет недостатки. Код, который вы должны прочитать из файла, выглядит правильно; Я бы предложил затем продолжить упражнение и проверить результаты, или, если вы действительно хотите быть уверенным, посмотрите на файл с помощью шестнадцатеричного редактора, и ваша программа выведет значения байтов, которые она читает (в виде чисел), - и сравните их с что вы видите в шестнадцатеричном редакторе.