Считать данные в текстовый файл и зарезервировать вывод с N символ - PullRequest
0 голосов
/ 15 мая 2019

Я хочу прочитать данные из консоли и вывести их в текстовый файл с резервом N символов на переменную структурного типа.
Текстовый файл похож на:

1  111 1 Peter
22 22  2 John Lays
3  3   3 Anne Belgs

Я не знаю, если яЯ использую самые правильные функции.
Также я не могу прочитать ("carro.name") более 1 слова (пример: Джон Лэйс)

struct estruturaCarro {
    int id, potencia, avariado;
    char name[11]; 
} carro;
...    
//Read data to Text File:
...
printf("\n ID......:"); scanf("%d", &carro.id);
printf("\n Potencia:"); scanf("%d", &carro.potencia);
printf("\n Avariado:"); scanf("%d", &carro.avariado);
printf("\n NAME:"); scanf("%10[0-9a-zA-Z ]", carro.name);  // or scanf("%[^\n]s",...)

fprintf(fp, "%-2d %-3d %-1d %-10s \n\n", carro.id, carro.potencia, carro.avariado, carro.name);
...
//Show File Text data:
...
int registos=0;
while(1)
{
    fscanf(fp, "%d %d %d %-10s", &carro.id, &carro.potencia, &carro.avariado, carro.name);
    if(feof(fp)){ break; }    
    printf("%-2d %-3d %-1d %-10s\n", carro.id, carro.potencia, carro.avariado, carro.name);
    registos++;
}

printf("\nCarros=%d", registos);

1 Ответ

1 голос
/ 15 мая 2019

Как вы говорите в своем вопросе, вы не можете использовать scanf для чтения сложного имени, включая пробелы.

Но прежде чем искать как сделать это, необходимо решить, что делать.

Возможно, вы не хотите запоминать лишние пробелы в начале и в конце (включая перевод строки), и, вероятно, имя не должно быть пустым.

Но как насчет сложного имени?Если пользователь вводит John Lays, сохраняете ли вы имя с двумя пробелами или хотите упростить, чтобы иметь только один?Вам нужно управлять другими специальными символами, такими как '-' (John - Lays / John- Lays / John -Lays читается как John-Lays?).

Что делать, если длина входной строки больше 10персонажи ?Просто прекратить читать, оставляя остальное для следующего чтения или обходить новую строку?Поскольку вы печатаете сообщение перед каждым вводом, вам явно требуется ввод для каждой строки, а остальная часть строки должна быть пропущена.

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

Вы также должны решить, что делать, если пользователь не введет число для ID или Potencia или Avariado, в настоящее время вы даже не обнаруживаете ошибку, это не очень хороший способ.Таким образом, в этом случае вы все отменяете (программа выхода), или вы переделываете чтение?Вероятно, вы предпочитаете читать снова, для этого вам нужно пропустить неверный ввод, но что это значит, чтобы обойти все до новой строки?

Например:

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

/* read an int memorizing its value in v,
   return 0 in case of EOF else a non null value */
int readInt(const char * msg, int * v)
{
  for (;;) {
    fputs(msg, stdout);
    if (scanf("%d", v) == 1)
      return 1;

    /* not a number or EOF, bypass all up to \n */
    int c;

    while ((c = fgetc(stdin)) != '\n')
      if (c == EOF)
        return 0;

    puts("invalid value"); /* message may be also get in argument */
  }
}

/* read a string up to a \n
   remove extra spaces at the beginning and end
   simplify internal multiple spaces
   accept any character and do not manage in a special way characters like like '-'
   a non empty string must be read
   read at most sz-1 characters in s then place the null character (as fgets), sz must be > 1
   if the line too long bypass the rest of the input up to \n
   return 0 in case of EOF else a non null value */
int readStr(const char * msg, char * s, size_t sz)
{
  fputs(msg, stdout);

  /* read the first char bypassing spaces including \n */
  if (scanf(" %c", s) == 0)
    // EOF
    return 0;

  size_t index = 1;
  int c;

  sz -= 1;

  while (index != sz) {
    c = fgetc(stdin);

    if ((c == EOF) || (c == '\n'))
      break;

    if (!isspace(c))
      s[index++] = c;
    else if (s[index - 1] != ' ')
      s[index++] = ' ';
  }

  s[(s[index - 1] != ' ') ? index : index-1] = 0;

  // bypass possible rest of the line
  while ((c != EOF) && (c != '\n'))
    c = fgetc(stdin);

  return 1;
}

/* ******************* */

struct estruturaCarro {
  int id, potencia, avariado;
  char name[11]; 
} carro;

int main()
{
  do {
    if (!readInt("\n ID......:", &carro.id) ||
        !readInt("\n Potencia:", &carro.potencia) ||
        !readInt("\n Avariado:", &carro.avariado) ||
        !readStr("\n NAME:", carro.name, sizeof(carro.name))) {
      puts("EOF");
      return -1;
    }
    else
      printf("%-2d %-3d %-1d '%-10s' \n\n", carro.id, carro.potencia, carro.avariado, carro.name);
  } while (strcmp(carro.name, "end"));

  return 0;
}

Компиляция иВыполнение:

pi@raspberrypi:/tmp $ gcc -pedantic -Wextra -Wall r.c
pi@raspberrypi:/tmp $ ./a.out

 ID......:aze
invalid value

 ID......:qsd
invalid value

 ID......:1

 Potencia:2

 Avariado:3

 NAME:aze            u iiiiiiiiiiiiiiiiii
1  2   3 'aze u iiii' 


 ID......:11

 Potencia:22

 Avariado:0

 NAME:    end
11 22  0 'end       ' 

pi@raspberrypi:/tmp $ 

Когда вы читаете в своем файле и предполагаете, что он был создан с помощью fprintf(fp, "%-2d %-3d %-1d %-10s", ...):

  char line[21]; /* each line has 20 characters newline included */

  while (fgets(line, sizeof(line), fp) != NULL) {
    if (sscanf(line, "%d %d %d", &carro.id, &carro.potencia, &carro.avariado) != 3)
      /* abnormal case, invalid file */
      break; /* anything else you want to do */
    /* the string starts at the index 9 and has 10 characters out of the newline */
    memcpy(carro.name, line + 9, 10);
    carro.name[10] = 0;
    /* ... */
  }

, обратите внимание, что имя имеет пробелы в конце, если его длина меньше10 символов

Или вы можете прочитать так же, как и в предыдущем случае на стандартном вводе.

...