C: Файловый ввод-вывод — пример кода из книги и реальность. Опустить .txt?

В настоящее время я изучаю главу 13 из книги C Primer Plus (6-е издание) Prata, листинг 13.2 на странице 574, реализованный в кодовых блоках с использованием gcc. Код показан ниже. У меня есть текстовый файл с именем eddy.txt (но Win10 не показывает окончание .txt, поэтому показывает только eddy). Программа использует этот текстовый файл в качестве аргумента через argc/argv[], считывает строку и сохраняет измененную версию в новый файл с именем eddy.red.

Просматривая объяснение книги, новый текстовый файл должен называться «eddy.red». Однако новый текстовый файл на моей машине называется «eddy.txt.red».

Есть ли у кого-нибудь из вас объяснение, почему существует разница между моим выводом и примером из книги? Это зависит от машины/компилятора? Похоже, что «.txt» исходного файла eddy.txt попал в имя строки, поэтому при создании вывода новый файл будет называться «eddy.txt»+».red» = «eddy.txt». .красный". Это немного прискорбно, потому что было бы здорово, если бы новый файл был напрямую сохранен как файл .txt, что было бы в том случае, если бы я просто выбрал имя для нового файла, например. "Алекс": fopen("Alex.txt","w") вместо fopen(имя, "w").

Спасибо,

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

#define LEN 50
/*
From C Primer Plus. Listing 13.2
*/

int main(int argc, char * argv[])
{
    FILE *in;
    FILE *out;
    int ch;
    char name[LEN];
    int count = 0;

    if(argc < 2)
    {
        fprintf(stderr, "Usage: %s filename\n", argv[0]);
        exit(EXIT_FAILURE);
    }

    if((in=fopen(argv[1],"r"))==NULL)
    {
        fprintf(stderr,"Couldn't open the file \"%s\"\n",argv[1]);
        exit(EXIT_FAILURE);
    }

    strncpy(name,argv[1],LEN-5);
    name[LEN-5] = '\0';
    strcat(name,".red");
    if((out=fopen(name,"w"))==NULL)
    {
        fprintf(stderr,"Cannot create output file\n");
        exit(EXIT_FAILURE);
    }
    // copy data
    while((ch=getc(in))!=EOF)
    {
        if(count++ % 3 == 0)
        {
            putc(ch, out);
        }
    }
    if(fclose(in)!=0 || fclose(out)!=0)
    {
        fprintf(stderr,"Error in closing files\n");
    }
    return 0;
}


person Alex    schedule 12.04.2020    source источник
comment
Боюсь, это исходит от вашей вашей... (извините, мне пришлось выбежать и блевать)... операционной системы. А если серьезно, зайдите в настройки проводника Windows и отключите параметр скрытия расширений для известных типов файлов. Тогда будет легче определить, когда приложение (например, ваш текстовый редактор) изменяет расширение файла, не сообщая вам об этом.   -  person prog-fh    schedule 12.04.2020
comment
Я уважаю ваше мнение о моей операционной системе. Итак, вы говорите, что строка файла в моей программе читается как eddy.txt, а не как eddy, что связано исключительно с тем, что я использую Windows? Если бы у меня была запущена система Linux, строка файла читалась бы eddy, а не eddy.txt?   -  person Alex    schedule 12.04.2020
comment
В моей шутке не было обиды, искренне. Просто я регулярно сталкиваюсь с такой ситуацией у людей (студентов, но не только), которые очень удивляются тому, что выбранное ими имя файла было заменено на другое, и этого не видно, потому что расширение файла скрыто. Итак, если ваше имя файла действительно eddy.txt, тогда ваша программа должна использовать точно такое же имя; скрытое расширение feature просто связано с тем, как файловый менеджер отображает файлы; ваша программа C использует фактическое имя файла.   -  person prog-fh    schedule 12.04.2020
comment
Без обид :) Я действительно понял, почему я получаю другой результат. Я использовал настоящий текстовый файл (eddy.txt), тогда как в книге используется файл eddy. Поэтому, если я удалю .txt из своего файла, у которого больше нет текстового файла, я могу получить тот же результат, что и в книге. Я пропустил это прекрасное, но важное отличие в книге. Спасибо.   -  person Alex    schedule 12.04.2020
comment
Какое имя вы указали в командной строке? Вы сказали eddy или eddy.txt? Похоже, вы сказали eddy.txt, и когда это было скопировано в name и добавлено расширение .red, вы получите eddy.txt.red, как и ожидалось. Если бы вы сказали просто eddy, я бы ожидал, что вы не сможете открыть ни один файл (если у вас есть файл eddy без суффикса).   -  person Jonathan Leffler    schedule 12.04.2020
comment
Привет, Джонатан - да, именно здесь я допустил ошибку, и ты прав со всеми своими предположениями. Теперь я могу полностью воспроизвести результат в книге. Спасибо вам обоим.   -  person Alex    schedule 12.04.2020


Ответы (1)


Если strlen(argv[1]) меньше LEN-5, то добавление ".red" не вызовет никаких проблем: места достаточно для 4 символов и конечного '\0'.
Но если argv[1] длиннее, то может произойти переполнение name.
Вот почему strncpy() используется с LEN-5 вместо strcpy().
Но strncpy() не гарантирует, что результирующая строка будет заканчиваться нулем, если будет достигнута максимальная вместимость.
Тогда безопасно (даже если это не совсем правильно) разместить '\0' за пять символов до конца (name[LEN-5] = '\0';).
Таким образом, мы уверены, что в худшем случае сможем добавить ".red" (даже если имя файла несколько укорочено).

Во многих случаях, когда имя файла короче, все это не имеет никакого эффекта, поскольку '\0' уже стоит перед LEN-5.
Таким образом, "eddy.txt" станет "eddy.txt.red".

person prog-fh    schedule 12.04.2020