将数据读取到文本文件并保留N个字符的输出

时间:2019-05-15 15:16:48

标签: c file struct scanf

我想从控制台读取数据并输出到文本文件,每个结构类型变量保留N个字符。
文本文件类似于:

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

我不知道我使用的是最正确的功能。
另外,我读(“ carro.name”)字数超过1个字(例如:John Lays)

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 个答案:

答案 0 :(得分:1)

正如您在问题中所说,您不能使用 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个字符,则该名称的末尾应带有空格

或者您可以按照与标准输入类似的方式阅读。