使用fscanf填充一个char数组会更改另一个char数组的值

时间:2017-05-14 14:11:21

标签: c arrays

我首先使用fscanf填充我的第一个数组然后再使用同一输入文件中的fscanf来填充另一个数组。然而,这似乎正在改变我的第一个数组中的值。

以下是我的意见:

4 
abcd
efgh
ijkl
mnop
qrst
uvwx
yz12
3456

这是我的代码:

#include <stdio.h>

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

int main(void){

    FILE *in = fopen("transform.in", "r");
    FILE *out = fopen("transform.out", "w");

    int num;
    fscanf(in, "%d", &num);

    char square[num][num];
    for (int i = 0; i < num; i++){
        fscanf(in, "%s", square[i]);
    }

    prints(num, square);
    printf("\n");

    char endSquare[num][num];
    for (int i = 0; i < num; i++){
        fscanf(in, "%s", endSquare[i]);
    }

    fclose(in);

    prints(num, square);
    printf("\n");

    prints(num, endSquare);
    printf("\n");

    fclose(out);

    return 0;
}

这是我得到的输出:

abcd
efgh
ijkl
mnop

bcd
efgh
ijkl
mnop

qrst
uvwx
yz12
3456

正如你所看到的,在我填充endSquare后,我的方阵似乎有所改变。

1 个答案:

答案 0 :(得分:1)

除了不考虑%s格式说明符将附加的 nul-terminatedating 字符外,您通过尝试读取行<来对自己造成极大的困难/ strong>具有格式化输入功能fscanf的数据。在进行面向行的输入时,最好使用面向行的输入函数,例如fgets,然后从包含的缓冲区中解析所需的信息整条信息。为什么?将数字和字符输入与scanf函数族混合可以为那些不考虑输入缓冲区中的所有字符的帐户构成许多陷阱,或说明不同的{{1 格式说明符处理前导空格

特别是在您阅读fscanf的情况下,您无法使用num 格式说明符限制fscanf读取的字符数。您不能包含变量字段宽度以防止写入超出数组边界。 (例如,您可以使用%s之类的%nums来确保您将读取的字符限制为%4s)使用VLA来保存根据从第一行读取的内容的特定字符数,只是没有一种优雅的方式来合并4并验证/限制使用num读取的字符数。

所有这些都会导致 train-wreck 等待发生,如果在你的一行的末尾发生了一个迷路fscanf(或其他字符)。< / p>

那么,如何处理space4-char的每一行square? (注意:大写endsquare被缩减为小写以匹配正常的C风格)当您需要处理输入行时,使用面向行的输入函数并提供足够的缓冲区处理每一行数据。我宁愿使用'S'缓冲区并确保每次128-char读取而不是意外地将4-5 char line行读入5-char缓冲区 - 以避免未定义的行为。此外,您可以使用相同的缓冲区来读取每一行数据。

接下来,您必须验证每次读取和每次转换,以确保您不会从代码中失败的读取或转发失败的角度处理垃圾。例如,在读取数据文件时,可以声明一个简单的缓冲区并按如下方式读取第一行:

4-char

在阅读每个后续行时,您需要通过检查尾随#define MAX 128 ... char buf[MAX] = ""; ... if (!fgets (buf, MAX, in)) { /* read 1st line with 'num' */ fprintf (stderr, "error: read of 'num' failed.\n"); return 1; } errno = 0; /* errno to check after strtol conversion */ int num = (int)strtol (buf, NULL, 10); /* convert num to int */ if (errno) { /* validate */ fprintf (stderr, "error: failed conversion of 'num'.\n"); return 1; } (由'\n'读取和包含)来确认是否已读取整行,如果它不存在,你的线太长(处理错误)。您还需要知道行中是否有fgets个字符,因为您没有将这些行存储为字符串(仅作为字符数组)。如果没有num读取,则无法将num-chars复制到num-charssquare[i]。由于您使用的是endsquare[i]循环,因此您还必须检查您读取的每一行是否为有效行。仅仅因为您将for读作第一行,就无法保证文件中有4行。 (理想情况下,您希望使用8循环来驱动其余输入,并在读取while (fgets (buf, MAX, in))后使用计数器中断,但您可以通过验证来在4-lines循环内进行保护也是for

考虑到这一点,您可以使用文件中的if (fgets (buf, MAX, in))读取来填充字符数组(或更少),同时在使用4-chars时检查任何意外的空行,如下所示循环:

for

您可以对 char square[num][num]; for (int i = 0; i < num; i++) { size_t len, n = num; if (!fgets (buf, MAX, in)) /* read line int buf */ break; /* break if no line read */ len = strlen (buf); /* get length */ if (buf[len - 1] != '\n') { /* if no '\n' at end, too long */ fprintf (stderr, "error: line[%d] too long.\n", i); return 1; } if (*buf == '\n') { /* 1st char is '\n' - empty */ fprintf (stderr, "error: empty line encountered.\n"); return 1; } if ((int)(len - 1) < num) /* if less than num, reduce num */ n = len - 1; memcpy (square[i], buf, n); /* copy 'num' chars from buf */ } 循环执行相同的操作。总而言之,您可以执行以下操作(注意: endsquare未使用,因此与其相关的代码将在下面注释掉):

out

注意:从不使用可变参数#include <stdio.h> #include <stdlib.h> #include <string.h> #include <errno.h> #define MAX 128 void prints (int n, char (*sqr)[n]) { for (int i = 0; i < n; i++) { for (int j = 0; j < n; j++) { putchar (sqr[i][j]); } putchar ('\n'); } } int main (int argc, char **argv) { char buf[MAX] = ""; FILE *in = fopen (argc > 1 ? argv[1] : "transform.in", "r"); // FILE *out = fopen ("transform.out", "w"); if (!in /* || !out */) { /* validate both files open */ fprintf (stderr, "error: file open failed.\n"); return 1; } if (!(fgets (buf, MAX, in))) { /* read 1st line with 'num' */ fprintf (stderr, "error: read of 'num' failed.\n"); return 1; } errno = 0; /* errno to check after strtol conversion */ int num = (int)strtol (buf, NULL, 10); /* convert num to int */ if (errno) { /* validate */ fprintf (stderr, "error: failed conversion of 'num'.\n"); return 1; } char square[num][num]; for (int i = 0; i < num; i++) { size_t len, n = num; if (!fgets (buf, MAX, in)) /* read line int buf */ break; /* break if no line read */ len = strlen (buf); /* get length */ if (buf[len - 1] != '\n') { /* if no '\n' at end, too long */ fprintf (stderr, "error: line[%d] too long.\n", i); return 1; } if (*buf == '\n') { /* 1st char is '\n' - empty */ fprintf (stderr, "error: empty line encountered.\n"); return 1; } if ((int)(len - 1) < num) /* if less than num, reduce num */ n = len - 1; memcpy (square[i], buf, n); /* copy 'num' chars from buf */ } prints (num, square); putchar ('\n'); char endsquare[num][num]; for (int i = 0; i < num; i++) { size_t len, n = num; if (!fgets (buf, MAX, in)) /* read line int buf */ break; /* break if no line read */ len = strlen (buf); /* get length */ if (buf[len - 1] != '\n') { /* if no '\n' at end, too long */ fprintf (stderr, "error: line[%d] too long.\n", i); return 1; } if (*buf == '\n') { /* 1st char is '\n' - empty */ fprintf (stderr, "error: empty line encountered.\n"); return 1; } if ((int)(len - 1) < num) /* if less than num, reduce num */ n = len - 1; memcpy (endsquare[i], buf, n); /* copy 'num' chars from buf */ } fclose(in); prints (num, square); putchar ('\n'); prints (num, endsquare); putchar ('\n'); // fclose(out); return 0; } 函数输出单个字符,而是使用一个用于输出单个字符的函数,如printf(或{{ 1}})。另请注意,如果在任何组中读取的行数少于putchar,那么您应该保留单个行的计数并将正确的数字传递给fputc

示例输入

prints

示例使用/输出

num

您可以执行更多验证,但至少上述内容将适用于您的数据并进行合理的错误检查。仔细看看,如果您有任何问题,请告诉我。

如果您想要打印正方形,您可以执行以下操作:

$ cat dat/list.txt
4
abcd
efgh
ijkl
mnop
qrst
uvwx
yz12
3456

示例w /已修改 $ ./bin/inout dat/list.txt abcd efgh ijkl mnop abcd efgh ijkl mnop qrst uvwx yz12 3456

void prints (int n, char (*sqr)[n])
{
    for (int i = 0; i < n; i++) {
        for (int j = 0; j < n; j++) {
            if (j)
                putchar (' ');
            putchar (sqr[i][j]);
        }
        putchar ('\n');
    }
}