CS50 Recover — как мне использовать sprintf? ошибка: использование необъявленного идентификатора ‘filename’; вы имели в виду ‘переименовать’?

#c #memory #printf #cs50

#c #память #printf #cs50

Вопрос:

CS50 — мой первый опыт программирования, и я не могу пройти восстановление. Я прохожу бесплатный онлайн-курс, работаю сам, и эти проблемы очень сложны для меня. Если мой код и вопросы выглядят новичками, я заранее приношу извинения.

Некоторые вопросы:

  1. Правильно ли я использую sprintf? Что мне нужно использовать вместо «filename» (см. Ошибка)
  2. Правильно ли я использую цикл while?
  3. Я даже близок к правильному решению или мне следует прекратить кодирование на этом этапе?
 #include <stdio.h>
#include <stdlib.h>

int main(int argc, char *argv[])
{
    // check usage
    if (argc != 2)
    {
        printf("Please enter only 1 argumentn");
        return 1;
    }
    // open file
    FILE *file = fopen(argv[1],"r");
    if (file == NULL)
    {
        printf("Usage: ./recover usagen");
        return 1;
    }

    int count = 0;
    // Read first 4 bytes
    unsigned char buffer[4];
    while(fread(buffer, 512, 1, file) == 512);
    // check first 4 bytes
    if (buffer[0] == 0xff amp;amp;
        buffer[1] == 0xd8 amp;amp;
        buffer[2] == 0xff amp;amp;
        (buffer[3] amp; 0xf0) == 0xe0)
        // count this 512B block
        {
            if (count == 0) // if first jpeg
            {
                sprintf(filename, "i.jpg", count); // last number is ith filename
                FILE *img = fopen(filename, "w"); // writing to filename
                fwrite(buffer, 512, 1, filename); // write to buffer 512 bytes at a time to filename
            }
            else
            {
                fclose(filename);
                count  ; // add to existing count
                sprintf(filename, "i.jpg", count); // last number is ith filename
                FILE *img = fopen(filename, "w"); // writing to filename
                fwrite(buffer, 512, 1, filename); // write to buffer 512 bytes at a time to filename
                
            }
        }
        else
        {
            if (count > 1)
            {
                sprintf(filename, "i.jpg", count); // last number is ith filename
                FILE *img = fopen(filename, "a"); // writing to filename
                fwrite(buffer, 512, 1, filename); // write to buffer 512 bytes at a time to filename
            }
        }

    fclose(argv[1]);
}
 

Вот мои коды ошибок:
~/pset4/recover/ $ make recover
clang -ggdb3 -O0 -std=c11 -Wall -Werror -Wextra -Wno-sign-compare -Wno-unused-parameter -Wno-unused-variable -Wshadow восстановить.c -lcrypt -lcs50 -lm -o восстановить
восстановить.c:33:25: ошибка: использование необъявленного идентификатора ‘filename’; вы имели в виду ‘переименовать’?
sprintf(filename, «i.jpg «, count); // последнее число — это i-е имя файла
^~~~~~~~
переименовать

recover.c:57:12: ошибка: несовместимые типы указателей, передающие ‘char *’ в параметр типа ‘FILE *’ (он же ‘struct _IO_FILE *’) [-Ошибка, -Wincompatible-pointer-types] fclose(argv[1]); ^~~~~~~/usr/include/stdio.h:199:26: примечание: передача аргумента параметру ‘__stream’ здесь extern int fclose (FILE *__stream); сгенерировано 11 ошибок. : не удалось выполнить рецепт для целевого ‘recover’ make: *** [восстановить] Ошибка 1

Комментарии:

1. Рискуя указать на очевидное, я не вижу ‘filename’, объявленного где-либо в вашем коде, поэтому первая ошибка не кажется неожиданной. Что вы подразумеваете под ‘filename’ для ссылки? (Редактировать: у меня есть предположение относительно того, на что оно должно ссылаться, но мне все еще интересно, каковы намерения.)

2. fclose Функция принимает a FILE* в качестве аргумента, а не строку имени файла. Так и fclose(argv[1]); должно быть fclose(file); .

3. @scg Спасибо за ваш ответ. Я полагаю, что проблема заключается в моем понимании того, что следует использовать здесь вместо filename. Из видео лекции указано, что это должен быть массив символов для хранения результирующей строки для этого jpg. Означает ли это, что мне нужно создать новый массив (т.е. buffer1). И здесь мне нужно использовать malloc для выделения памяти?

4. @AdrianMole Спасибо. Извините, что спрашиваю очевидное, но у меня действительно проблемы с именами здесь. Вы буквально имеете в виду fclose (файл); или это должно быть fclose (card.raw)?

Ответ №1:

В вашем коде есть ряд проблем. В некоторых есть «простые» исправления, в других я могу только делать «разумные догадки».

Во-первых, вы используете filename переменную, в которую записываете имя ваших выходных файлов, но никогда не объявляете это; это простое решение: объявите char filename[512]; переменную (или любой другой размер, вместо 512, который вы считаете необходимым).

Во-вторых, fclose функция принимает в качестве своего аргумента ранее открытый FILE* ‘дескриптор’; в вашем коде это будет либо file переменная (для входного файла), либо img переменная (для выходных данных).

В-третьих, ваш код использует очень «локальные» определения для img переменной, которые не являются общими для разных if ... else блоков; чтобы исправить это, объявите переменную в более «внешней» области видимости и просто используйте ее (без повторного объявления) в других местах.

Другие проблемы заключаются в том, что ваш while (fread(buffer, 512, 1, file) == 512); оператор читается много раз (вероятно), но следующий код выполняется только после сбоя этой операции чтения, что почти наверняка не то, что вам нужно; итак, у вас должен быть { ... } блок для включения (большей части) следующего кода в этот while цикл.

Есть также несколько мест, где, я думаю, вам следует закрыть выходной файл: один раз внутри цикла, где вы «повторно открываете» новый вывод, и один раз после завершения всего большого цикла, чтобы закрыть «висячий» дескриптор открытого файла.

Я добавил комментарии с тройной косой чертой ( /// ) в приведенный ниже код, чтобы решить эти вопросы. Пожалуйста, не стесняйтесь запрашивать какие-либо дополнительные разъяснения и / или объяснения.

 int main(int argc, char* argv[])
{
    // check usage
    if (argc != 2) {
        printf("Please enter only 1 argumentn");
        return 1;
    }
    // open file
    FILE* file = fopen(argv[1], "r");
    if (file == NULL) {
        printf("Usage: ./recover usagen");
        return 1;
    }

    int count = 0;
    /// TEST first 4 bytes
    unsigned char buffer[512]; /// This needs to be 512 bytes - not just the first 4 that you test!!
    char filename[512]; /// Buffer in which to write filename string
    FILE* img = NULL;   /// This variable MUST be in the more outer scope!
    while (fread(buffer, 1, 512, file) == 512) { /// count and size were round the wrong way!
        // check first 4 bytes
        if (buffer[0] == 0xff amp;amp;
            buffer[1] == 0xd8 amp;amp;
            buffer[2] == 0xff amp;amp;
            (buffer[3] amp; 0xf0) == 0xe0)
            // count this 512B block
        {
            if (count == 0) { // if first jpeg
                sprintf(filename, "i.jpg", count); // last number is ith filename
                img = fopen(filename, "w"); /// Don't (re)declare a local "img"
                fwrite(buffer, 1, 512, img); /// Use FILE* handle, not name!
            }
            else {
                fclose(img); /// Use FILE* handle, not name!
                count  ; // add to existing count
                sprintf(filename, "i.jpg", count); // last number is ith filename
                img = fopen(filename, "w"); /// Don't (re)declare a local "img"
                fwrite(buffer, 1, 512, img); /// Use FILE* handle, not name!
            }
        }
        else {
            if (count > 1) {
                fclose(img); /// I THINK you need to close the old file first!
                sprintf(filename, "i.jpg", count); // last number is ith filename
                img = fopen(filename, "a"); // writing to filename
                fwrite(buffer, 1, 512, img); // write to buffer 512 bytes at a time to filename
            }
        }
    }
    fclose(img); /// At SOME point (probably here) you should close the remaining opened output file!

    fclose(file); /// Use FILE* handle, not name!
    return 0;
}
 

Кроме того, чтобы быть тщательным, вы действительно должны проверять каждый результат img - fopen(...) вызовов и добавлять какую-либо обработку ошибок, если это когда-либо NULL (как вы делали при открытии входного файла).

Комментарии:

1. Кроме того, (замечено после публикации) ваша buffer переменная должна быть 512 байт, потому что именно столько вы закачиваете в нее при каждой операции ‘fread’ и сколько вы записываете из нее в fwrite вызовах.

2. И еще одно «позднее место» — ваши count size аргументы и для fread и fwrite округлены неправильно (отредактировано в ответе). Это будет иметь значение в fread тесте, потому что оно возвращает количество элементов, а не количество прочитанных байтов .

3. Я не могу описать, как я благодарен за ваши ответы. Я все еще регистрирую их все. Размер буфера сбивал меня с толку, но теперь это имеет смысл. Теперь я добавляю проверки для каждого результата вызовов img — fopen (…). Используя ваш код, он возвращает ошибку сегментации. Я предполагаю, что это как-то связано с этим.

4. Я не уверен, что происходило раньше, но я больше не получаю ошибку сегментации. и был создан первый jpg! Теперь я вижу 000.jpg , но ничего после этого. Мне интересно, не сломано ли что-нибудь в цикле. Проверка прямо сейчас.

5. @Arian Mole Я думаю, что я только что нашел это! Отсутствует счетчик в предложении (count == 0) .

Ответ №2:

Благодаря указателям Адриана Моула я завершил свой код следующим образом:

 #include <stdio.h>
#include <stdlib.h>
#include <string.h>

int main(int argc, char *argv[])
{
    // check usage
    if (argc != 2) 
    {
        printf("Please enter only 1 argumentn");
        return 1;
    }
    // open file
    FILE *file = fopen(argv[1], "r");
    if (file == NULL) 
    {
        printf("Usage: ./recover usagen");
        return 1;
    }
    int count = 0; // create count integer
    unsigned char buffer[512]; /// This needs to be 512 bytes - not just the first 4 that you test!!
    char filename[512]; // Buffer in which to write filename string
    FILE *img = NULL;   // This variable MUST be in the more outer scope!
    while (fread(buffer, 1, 512, file) == 512) // count and size were round the wrong way!
    { 
        // check first 4 bytes
        if (buffer[0] == 0xff amp;amp;
            buffer[1] == 0xd8 amp;amp;
            buffer[2] == 0xff amp;amp;
            (buffer[3] amp; 0xf0) == 0xe0)
            // count this 512B block
        {
            if (count == 0) // if first jpeg
            { 
                count  ;
                sprintf(filename, "i.jpg", count - 1); // last number is ith filename
                img = fopen(filename, "w"); // Don't (re)declare a local "img"
                fwrite(buffer, 1, 512, img); // using img handle
            }
            else 
            {
                fclose(img); /// Use FILE* handle, not name!
                count  ; // add to existing count
                sprintf(filename, "i.jpg", count - 1); // last number is ith filename
                img = fopen(filename, "w"); // Don't (re)declare a local "img"
                fwrite(buffer, 1, 512, img); // Using img handle
            }
        }
        else 
        {
            if (count > 0) 
            {
                fwrite(buffer, 1, 512, img); // write to buffer 512 bytes at a time to filename
            }
            else
            {
                continue;
            }
        }
    }
    fclose(img); // At SOME point (probably here) you should close the remaining opened output file!

    fclose(file); // Use FILE* handle, not name!
    return 0;
}