【问题标题】:How do I read integers and characters from a text file and store them in separate arrays?如何从文本文件中读取整数和字符并将它们存储在单独的数组中?
【发布时间】:2020-03-04 11:18:50
【问题描述】:

我正在尝试从包含诸如“Jane 30”、“Chris 40”等字符串的文件中逐行读取输入。然后我需要将每个名称及其对应的数字存储在不同数组的同一索引中,因此“Jane”在一个索引为 0 的数组中,30 在一个索引为 0 的整数数组中。

这是我到目前为止的代码,但我正在努力弄清楚如何将整数提取到一个单独的数组中,并将字符提取到另一个数组中。请帮忙。

#include stdio.h
#include stdlib.h
#include DarrensInfo.

int main()
{
FILE * coinfile;
coinfile = fopen("coins.txt", "r");
char names[150];
int change[50];
int x, y;

while(!feof(coinfile)){

    fgets(names, 150, coinfile);
    y = 0;

    for(x=0; names[x]; x++){

        if(names[x] <= '0' && names[x] <= '9'){

            change[y] = names[x];
            y++;

        }
    }

}

fclose(coinfile);

return 0;
}

【问题讨论】:

标签: c arrays file


【解决方案1】:
#define COINS_MAX_LINES 150
#define MAX_LINE_LENGTH 100  
#define MAX_NAME_LENGTH 50
int main()
{
    FILE * coinfile;
    coinfile = fopen("coins.txt", "r");
    char line[MAX_LINE_LENGTH];
    char names[COINS_MAX_LINES][MAX_NAME_LENGTH];
    int change[COINS_MAX_LINES];
    int lineno = 0;
    int i = 0;
    while(fgets(line, MAX_LINE_LENGTH, coinfile))
    {
        sscanf(line, "%s %d", names[lineno], &change[lineno]);
        ++lineno;
    }

    fclose(coinfile);

    for (i = 0; i<lineno;++i)
        printf("Name = %s Change = %d\n", names[i], change[i]);

    return 0;

}

while 循环结束后,名称数组和更改数组将包含您想要的内容。我已经在第二个循环中打印出来了

【讨论】:

  • change数组的长度应该是COINS_MAX_LINES,检查scanf的返回值是个好主意(即使在示例中)。
  • sscanf(line, "%s ... 没有宽度限制(并且 MAX_LINE_LENGTH > MAX_NAME_LENGTH)与gets() 面临同样的麻烦。更好的代码可以避免缓冲区溢出。
【解决方案2】:
  1. 您需要一个字符数组。将变量names定义为names[150][30],假设每个名字的长度不超过30个字符。
  2. 结合使用 fgetsfscanf 将文件中的条目行解析为所需的单独变量
#include <stdio.h>
#include <stdlib.h>

int main()
{
  FILE * coinfile;
  coinfile = fopen("coins.txt", "r");
  char names[150][30];
  int change[150];
  int x = 0, y, i;

  char buf[100];
  while(!feof(coinfile)){
    if(fgets(buf, 100, coinfile) != NULL) { 
      sscanf(buf, "%s %d", names[x], &change[x]);
      x++;
    }
  }
  fclose(coinfile);

  puts("-------");
  for (int i = 0; i < x; i++)
    printf("%s %d\n", names[i], change[i]);

  return 0;
}

【讨论】:

  • 也许可以将while(!feof(coinfile)){ if(fgets(buf, 100, coinfile) != NULL) { 简化为while(fgets(buf, 100, coinfile)) {
【解决方案3】:

我正在尝试逐行读取包含“Jane 30”、“Chris 40”等字符串的文件中的输入

您正在读取的文件可能包含“Jane 30”、“Chris 40”等字符串;但也可能包含数百万个打字错误和/或其他错误;因此,您需要检测错误并清楚地告知用户错误是什么,以便他们可以轻松理解问题,然后找到错误,然后修复它。

因此,没有任何 C 库函数有用。

而是将解析器构建为有限状态机。例如(未经测试):

    // State

    int state = 0;
    int column = 0;
    int line = 1;
    char current_name[MAX_NAME_LENGTH];
    int name_length;
    int number;

    // Main loop

    for(;;) {
        int c = fgetc(file);
        column++;
        switch(state) {

        case 0:  /* At start of new line */
            if(c == FEOF) {
                return OK;
            } else if(isdigit(c)) {
                printf("ERROR: Number found at start of line (missing name), on line %d at column %d\n", line, column);
                return NOT_OK;
            } else if(isalpha(c)) {
                name_length = 0;
                current_name[name_length++] = c;
                state = 1;
            } else if(c == '\n') {
                line++
            } else if(isspace(c)) {
            } else {
                printf("ERROR: Bad character at start of line, on line %d at column %d\n", line, column);
                return NOT_OK;
            }
            break;

        case 1:  /* In middle of name */
            if(c == FEOF) {
                printf("ERROR: File ends in the middle of a name, on line %d at column %d\n", line, column);
                return NOT_OK;
            } else if(isdigit(c)) {
                printf("ERROR: No whitespace between name and number, on line %d at column %d\n", line, column);
                return NOT_OK;
            } else if(isalpha(c)) {
                if(name_length >= MAX_NAME_LENGTH) {
                    printf("ERROR: Name too long (max length is %d), on line %d at column %d\n", MAX_NAME_LENGTH, line, column);
                    return NOT_OK;
                }
                current_name[name_length++] = c;
            } else if(c == '\n') {
                printf("ERROR: No number after name, on line %d at column %d\n", line, column);
                return NOT_OK;
            } else if(isspace(c)) {
                state = 2;
            } else {
                printf("ERROR: Bad character in middle of name, on line %d at column %d\n", line, column);
                return NOT_OK;
            }
            break;

        case 2:  /* Between name and number */
            if(c == FEOF) {
                printf("ERROR: File ends after name, on line %d at column %d\n", line, column);
                return NOT_OK;
            } else if(isdigit(c)) {
                number = c - '0';
                state = 3;
            } else if(c == '\n') {
                printf("ERROR: No number after name, on line %d at column %d\n", line, column);
                return NOT_OK;
            } else if(isspace(c)) {
            } else {
                printf("ERROR: Bad character after name, on line %d at column %d\n", line, column);
                return NOT_OK;
            }
            break;

        case 4:  /* In middle of number */
            if(c == FEOF) {
                printf("ERROR: File ends in middle of number, on line %d at column %d\n", line, column);
                return NOT_OK;
            } else if(isdigit(c)) {
                if(number > INT_MAX / 10) {
                    printf("ERROR: Number is too large, on line %d at column %d\n", line, column);
                    return NOT_OK;
                }
                number *= 10;
                if(number > INT_MAX - (c - '0') ) {
                    printf("ERROR: Number is too large, on line %d at column %d\n", line, column);
                    return NOT_OK;
                }
                number += c - '0';
            } else if(c == '\n') {
                create_new_entry(current_name, name_length, number);
                line++
                state = 0;
            } else if(isspace(c)) {
                state = 5;
            } else {
                printf("ERROR: Bad character after number, on line %d at column %d\n", line, column);
                return NOT_OK;
            }
            break;

        case 5:  /* Trailing white space before end of line */
            if(c == FEOF) {
                printf("ERROR: File ends between number and end of line, on line %d at column %d\n", line, column);
                return NOT_OK;
            } else if(c == '\n') {
                line++
                create_new_entry(current_name, name_length, number);
                state = 0;
            } else if(isspace(c)) {
            } else {
                printf("ERROR: Unknown characters between number and end of line, on line %d at column %d\n", line, column);
                return NOT_OK;
            }

        }
    }

【讨论】:

    【解决方案4】:

    如果您尝试解析可能看起来像“King Charles 3”的名称,则很难使用 scanf。在不使用静态大小的缓冲区的情况下做这种事情实际上并不难,这是一个习惯的好习惯。您将要避免使用fgets,因为如果没有固定大小,这很难使用。请注意,众所周知,增长数组非常困难,因此我不声明以下内容的正确性。人们必须每隔几个月做一次这样的练习,以提醒我们为什么不在 C 中做这种事情:

    #include <ctype.h>
    #include <stdio.h>
    #include <stdlib.h>
    #include <string.h>
    
    FILE * Fopen(const char *, const char *);
    void * Realloc(void *, size_t);
    
    /* Remove trailing whitespace */
    void trim_white(char *b, char *end)
    {
            while( end > b && isspace(*--end)) {
                    *end = '\0';
            }
    }
    
    int
    main(int argc, char **argv)
    {
            char *path = argc > 1 ? argv[1] : "stdin";
            FILE *coinfile = argc > 1 ? Fopen(path, "r") : stdin;
            size_t asize = 16;
            size_t bsize = 0;
            char *buf = NULL;
            char **names = Realloc(NULL, asize * sizeof *names);
            long *change = Realloc(NULL, asize * sizeof *change);
            unsigned line_number = 0;
            ssize_t char_read;
            while( buf = NULL, (char_read = getline(&buf, &bsize, coinfile)) != -1) {
                    char *space;
                    char *end;
                    trim_white(buf, buf + char_read);
                    space = strrchr(buf, ' ');
                    if(space == NULL) {
                            fprintf(stderr, "Invalid input on line %d (no space)\n", line_number + 1);
                            exit(EXIT_FAILURE);
                    }
                    change[line_number] = strtol(space + 1, &end, 10);
                    if(*end != '\0') {
                            fprintf(stderr, "Invalid input on line %d at '%s'\n", line_number + 1, space + 1);
                            exit(EXIT_FAILURE);
                    }
                    *space = '\0';
                    names[line_number] = buf;
    
                    if(++line_number == asize) {
                            asize *= 2;
                            names = Realloc(names, asize * sizeof *names);
                            change = Realloc(change, asize * sizeof *change);
                    }
            }
    
            return EXIT_SUCCESS;
    }
    
    FILE *
    Fopen(const char *path, const char *mode) {
            FILE *fp = fopen(path, mode);
            if( fp == NULL ) { perror(path); exit(EXIT_FAILURE); }
            return fp;
    }
    
    void *
    Realloc(void *buf, size_t s)
    {
            buf = realloc( buf, s );
            if( buf == NULL) { perror("realloc"); exit(EXIT_FAILURE); }
            return buf;
    }
    

    【讨论】:

    • “你会想要避免 fgets”与getline()getline() 的一个弱点是允许恶意用户通过形成过长的输入行来压倒内存资源。 fgets() 确实提供了一个上限。每个解决方案都有其优点/缺点。 (旁白:建议 %u 使用无符号)
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-06-09
    • 1970-01-01
    • 1970-01-01
    • 2012-11-27
    • 1970-01-01
    • 2014-08-14
    相关资源
    最近更新 更多