Работа с текстовыми файлами Два

Пара вопросов действительно о приведенном ниже коде, из которого я получил помощь в предыдущем посте.

1). Любые идеи, почему в конце вывода я получаю случайный символ мусора? Я освобождаю файлы и т. д. и проверяю наличие EOF.

2). Идея состоит в том, что он может работать с несколькими аргументами файла, поэтому я хочу создать новые имена файлов, которые увеличиваются, то есть out[i].txt, возможно ли это в C?

Сам код берет файл, содержащий слова, разделенные пробелами, как, например, книга, затем перебирает и заменяет каждый пробел на \n, чтобы сформировать список, пожалуйста, найдите код ниже:

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

/*
 * 
 */
int main(int argc, char** argv) {

FILE *fpIn, *fpOut;
int i;
char c;
while(argc--) {
    for(i = 1; i <= argc; i++) {
        fpIn = fopen(argv[i], "rb");
        fpOut= fopen("tmp.out", "wb");
        while (c != EOF) {
            c = fgetc(fpIn);
            if (isspace(c)) 
                c = '\n';
            fputc(c, fpOut );
        }
    }
}
fclose(fpIn);
fclose(fpOut);
return 0;
}

person PnP    schedule 27.12.2011    source источник
comment
На самом деле это не так, я собираю коллекцию списков слов для целей сетевого тестирования и решил, что прогон некоторых книг с помощью этой маленькой программы создаст несколько отличных списков.   -  person PnP    schedule 28.12.2011


Ответы (2)


Когда вы достигаете конца файла, вы не break зацикливаетесь. Итак, вы вызываете fputc(c, fpOut); с c==EOF, что, вероятно, является неопределенным поведением или, по крайней мере, записью \0xff байта.

И вы не вызываете fclose внутри своего цикла while(argc--), поэтому ваши файлы (кроме последнего) в основном никогда не закрываются и не сбрасываются.

Наконец, вы не проверяете результат fopen, и вы должны проверить, что он не нулевой (и вывести сообщение об ошибке, возможно, с чем-то о strerror(errno) или perror в этом случае).

Вы должны были выяснить это с помощью отладчика (например, gdb в Linux) и, возможно, с помощью предупреждений компилятора (но gcc-4.6 -Wall не обнаружил на вашем примере никаких ошибок).

Вы можете решить, что имя выходного файла связано с именем входного файла, возможно, с

char outname[512];
for(i = 1; i < argc; i++) {
   fpIn = fopen(argv[i], "rb");
   if (!fpIn) { perror (argv[i]); exit(1); };
   memset (outname, 0, sizeof (outname));
   snprintf (outname, sizeof(outname)-1, "%s~%d.out", argv[i], i);
   fpOut= fopen(outname, "wb");
   if (!fpOut) { perror (outname); exit(1); };
   /// etc...
   fclose(fpIn);
   fclose(fpOut);
   fpIn = fpOut = NULL;
}
person Basile Starynkevitch    schedule 27.12.2011
comment
Кроме того, любые идеи для Q1 в моем описании, спасибо за вышеизложенное, это было очень полезно :) - person PnP; 28.12.2011
comment
Ваш мусорный персонаж, вероятно, \0xff, о котором я упоминал. На моей машине это, вероятно, ÿ; а не fflush-ing или fclose-ing файлы делают вывод произвольно усеченным. - person Basile Starynkevitch; 28.12.2011
comment
Ого, я хотел сказать Q2, вы уже дали отличный ответ на 1 в своем ответе :) - person PnP; 28.12.2011
comment
Я отредактировал свой ответ, чтобы ответить на вопрос 2, который я не совсем понял (я только догадываюсь, что вы, вероятно, имеете в виду). - person Basile Starynkevitch; 28.12.2011
comment
Вам не нужно обнулять outname, если вы все равно собираетесь вызывать snprintf в буфере. Кроме того, snprintf ожидает размер буфера, а не размер доступного для записи буфера (он будет записывать не более n-1 строки, сохраняя последний байт для нулевого символа). - person dreamlax; 28.12.2011
comment
memset не бесполезен. Это гарантирует, что последний байт равен \0 = нулевому байту для завершения строки (даже если snprintf не хватает места), потому что мне нужно, чтобы snprintf не потреблял все outname ! И это намного безопаснее. - person Basile Starynkevitch; 28.12.2011
comment
@BasileStarynkevitch: Почему бы тогда просто не установить последний байт, а не все сразу? Нет необходимости устанавливать их все равными нулю, если snprintf все равно будет писать поверх них. Кроме того, snprintf всегда гарантирует, что вывод завершается нулем, записывая не более n - 1 символов. То есть, если у вас есть буфер размером 5, он запишет максимум 4 байта и сохранит последний байт для нулевого символа. - person dreamlax; 29.12.2011
comment
Согласовано. На самом деле, я забыл, что snprintf всегда добавляет ноль байтов (а strncpy нет), даже при достижении максимального размера. - person Basile Starynkevitch; 29.12.2011

Предлагаемые изменения (все непроверенные):

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

int 
main(int argc, char** argv) {

  FILE *fpIn, *fpOut;
  int i;
  char c;
  for(i = 1; i < argc; i++) {
    fpIn = fopen(argv[i], "rb");
    if (!fpIn) {
      perror ("Unable to open input file");
      continue;
     }
    fpOut= fopen("tmp.out", "wb");
    if (!fpOut) {
      perror ("Unable to open output file");
      fclose (fpIn);
      continue;
     }
     while ((c = fgetc (fpIn)) != EOF)) {
       if (isspace(c)) 
         c = '\n';
       fputc(c, fpOut );
     }
     fclose(fpIn);
     fclose(fpOut);
  }
  return 0;
}
person paulsm4    schedule 27.12.2011