如何逐行读取数字文本文件并将其存储在C中的数组中?

时间:2017-10-28 03:23:43

标签: c

我必须读取一个充满数字的文本文件,并将每个数字存储到字符串数组中然后显示它。最终我想将数字转换为整数但是现在我只想将它们放入一个字符串数组中。我的程序只显示一半的数字以及一堆不需要的空行。这就是我到目前为止所拥有的:

void displayArray (char *a, int j) {
    for (int i = 0; i < j; i++) {
        printf("%c\n", a[i]);
    }
}

int main(int argc, const char *argv[]) {
    char temp[3];
    char str[26][3];
    int i = 0;

    // open file
    FILE *fp;
    fp = fopen("data.txt", "r");

    // access file
    while (!feof(fp)) {
        fgets(temp, 26, fp);
        //puts(temp);
        strcpy(str[i], temp);
        i++;
    }
    fclose(fp);

    displayArray(str, 26);

    return 0;
}

数据文件是:

8
2
0
10
.5
3
7
3
7
12
12
12
12
1
10
.5
12
12 
12
12
7
3
7
3

2 个答案:

答案 0 :(得分:1)

您有两个主要问题:(1)str中的列宽不足以容纳1, 0, \n, \0所需的10。在您阅读1, 0, \0时,'\n'将在您的输入流中保持未读状态。要更正此问题,请增加列宽。

(永远不要吝啬缓冲区大小,如果你认为你可以阅读3-4个字符,声明缓冲区8左右)

其次,您无法删除尾随的'\n'读取,默认情况下包含在str[x]中。 (所有面向行的函数,例如fgets和POSIX getline读取并在其填充的缓冲区中包含'\n'。修剪换行很简单。获取length,通过选中'\n'检查缓冲区中最后一个字符是str[n][length-1]的最后一个字符。如果是'\n',则通过 nul-terminated覆盖来删除它字符,例如

    /* read each line up to MAXL lines */
    while (n < MAXL && fgets (str[n], MAXC, fp)) {
        size_t len = strlen (str[n]);   /* get length */
        long tmp;       /* tmp value for conversion */
        errno = 0;      /* reset errno before each conversion     */
        if (len && str[n][len-1] == '\n')   /* is last char '\n'? */
            str[n][--len] = 0;         /* overwrite with nul-char */
        else {           /* the string was too long, handle error */
            fprintf (stderr, "error: value at line %d too long.\n", n);
            return 1;
        }

注意:如果最后一个字符不是'\n',你还可以确认缓冲区的行是否太长了

接下来,您无法验证fgets的返回。阅读可能会失败,您仍然会尝试复制temp,例如(strcpy(str[i], temp);)。您必须验证所有用户输入,否则您不能完全放弃从那一点开始处理垃圾。

您可以使用strtol将字符串转换为long。使用临时longstrtol返回,然后,在检查errno以验证转换后,您可以在将有效整数的范围内确认返回的数字,然后再将其分配给您整数数组,例如

        tmp = strtol (str[n], NULL, BASE);  /* convert to long */
        if (errno) {                        /* validate conversion */
            fprintf (stderr, "error: conversion to int failed '%s'.\n",
                    str[n]);
            return 1;
        }   /* validate in integer range */
        if (tmp < INT_MIN || tmp > INT_MAX) {
            fprintf (stderr, "error arr[%ld] exceeds range of int.\n", 
                    tmp);
            return 1;
        }
        arr[n++] = tmp; /* assign converted & validated value to array */

完全放弃,您可以读取您的文件(或从stdin),将读取的值存储为字符串,并将字符串转换为整数值,如下所示:

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

enum { MAXC = 8, BASE = 10, MAXL = 26 };

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

    int n = 0,                      /* array index */
        arr[MAXL] = {0};            /* array of integers */
    char str[MAXL][MAXC] = {""};    /* array of strings */
    FILE *fp = argc > 1 ? fopen (argv[1], "r") : stdin;

    if (!fp) {  /* validate file open for reading */
        fprintf (stderr, "error: file open failed '%s'.\n", argv[1]);
        return 1;
    }

    /* read each line up to MAXL lines */
    while (n < MAXL && fgets (str[n], MAXC, fp)) {
        size_t len = strlen (str[n]);   /* get length */
        long tmp;       /* tmp value for conversion */
        errno = 0;      /* reset errno before each */
        if (len && str[n][len-1] == '\n')   /* is last char '\n'? */
            str[n][--len] = 0;         /* overwrite with nul-char */
        else {           /* the string was too long, handle error */
            fprintf (stderr, "error: value at line %d too long.\n", n);
            return 1;
        }
        tmp = strtol (str[n], NULL, BASE);  /* convert to long */
        if (errno) {                        /* validate conversion */
            fprintf (stderr, "error: conversion to int failed '%s'.\n",
                    str[n]);
            return 1;
        }   /* validate in integer range */
        if (tmp < INT_MIN || tmp > INT_MAX) {
            fprintf (stderr, "error arr[%ld] exceeds range of int.\n", 
                    tmp);
            return 1;
        }
        arr[n++] = tmp; /* assign converted & validated value to array */
    }
    if (fp != stdin) fclose (fp);   /* close file if not stdin */

    for (int i = 0; i < n; i++)     /* output both string and ints */
        printf ("str[%2d] : %-3s    arr[%2d] : %3d\n",
                i, str[i], i, arr[i]);

    return 0;
}

示例使用/输出

$ ./bin/cnvstrtoint < ../dat/numdata.txt
str[ 0] : 8      arr[ 0] :   8
str[ 1] : 2      arr[ 1] :   2
str[ 2] : 0      arr[ 2] :   0
str[ 3] : 10     arr[ 3] :  10
str[ 4] : .5     arr[ 4] :   0
str[ 5] : 3      arr[ 5] :   3
str[ 6] : 7      arr[ 6] :   7
str[ 7] : 3      arr[ 7] :   3
str[ 8] : 7      arr[ 8] :   7
str[ 9] : 12     arr[ 9] :  12
str[10] : 12     arr[10] :  12
str[11] : 12     arr[11] :  12
str[12] : 12     arr[12] :  12
str[13] : 1      arr[13] :   1
str[14] : 10     arr[14] :  10
str[15] : .5     arr[15] :   0
str[16] : 12     arr[16] :  12
str[17] : 12     arr[17] :  12
str[18] : 12     arr[18] :  12
str[19] : 12     arr[19] :  12
str[20] : 7      arr[20] :   7
str[21] : 3      arr[21] :   3
str[22] : 7      arr[22] :   7
str[23] : 3      arr[23] :   3

注意: .5的整数转换结果为0,因此如果需要存储浮点值,则必须声明数组以保存转换后的值到doublefloat并使用strtod之类的浮点转换进行转换。

仔细看看,如果您有其他问题,请告诉我。

处理非长整数行

根据您的实施情况,strtol可能不会接受并截断.5,而是可能设置errno。您可以灵活地以任何方式处理失败的转换。例如,如果要在失败的转换上手动分配0并保持索引与读取的行数一致,则可以在失败的转换上警告并手动分配{{1例如

0

(输出相同)

另一方面,如果您只是想跳过 if (errno) { /* validate conversion */ /* if you want to assing zero on a failed conversion, warn */ fprintf (stderr, "warning: conversion to int failed '%s' - " "assigning 0.\n", str[n]); tmp = 0; } 未转换的任何值,您只需警告strtol(该行已经过了由continue读取,继续前进到文件中的下一行):

fgets

示例使用/输出(跳过非长整数值

    if (errno) {                        /* validate conversion */
        /* if you want to skip the value entirely, just continue */
        fprintf (stderr, "warning: conversion to int failed '%s' - "
                "skipping.\n", str[n]);
        continue;
    }   /* validate in integer range */

第二次看事情并告诉我你是否还有其他问题。

答案 1 :(得分:0)

您的代码中存在多个问题:

  • while(!feof(fp))不起作用:feof(fp)在尝试读取文件失败后返回true。使用fgets()并检查返回值。 NULL表示文件结束或读取错误。
  • char str[3]太短,无法容纳2位数字,因为换行符也存储在目标数组中。使这个数组更大。
  • 如果文件超过26行,您的程序将具有未定义的行为。您应该测试行的nuber或重新分配目标数组。
  • 输出循环的原型和实现不正确:它应该是:

    void displayArray(char a[][3], int j) {
        for (int i = 0; i < j; i++) {
            printf("%s\n", a[i]);
        }
    }
    

这是一种读取任意长度的字符串数组的简单方法:

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

int main(int argc, char *argv[]) {
    char buf[100];
    char *array = NULL;
    size_t i, count = 0;
    FILE *fp = stdin;

    if (argc > 1) {
        fp = fopen(argv[1], "r");
        if (fp == NULL) {
            fprintf(stderr, "cannot open %s: %s\n", argv[1],
                    strerror(errno));
            return 1;
        }
    }

    while (fgets(buf, sizeof buf, fp)) {
        buf[strcspn(buf, "\n")] = '\0';  // strip the newline if any
        array = realloc(array, sizeof(*array) * (count + 2));
        array[count++] = strdup(buf);
    }

    if (fp != stdin)
        fclose(fp);

    for (i = 0; i < count; i++) {
        printf("%s\n", array[i]);
    }

    free(array);

    return 0;
}

注意:

  • 此代码处理的最大行长度仍有98个字节的限制。
  • 应测试并报告内存分配失败。
  • 如果目标是阅读数字,转换应该在阅读循环中进行,以避免无用的并发症。