使用fscanf填充一个char数组会更改另一个char数组的值
我首先使用fscanf填充我的第一个数组,然后再使用同一输入文件中的fscanf来填充另一个数组。 然而,这似乎正在改变我的第一个数组中的值。
这是我的意见:
4 abcd efgh ijkl mnop qrst uvwx yz12 3456
这是我的代码:
#include 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后我的方阵似乎有所改变。
除了不考虑%s
格式说明符将附加的空终止字符之外,您通过尝试使用格式化输入函数fscanf
读取数据行来使自己变得非常困难。 在进行面向行的输入时,最好使用面向行的输入函数(如fgets
,然后从包含整行信息的缓冲区中解析所需的信息。 为什么? 将数字和字符输入与scanf
系列函数混合可以为那些不考虑保留在输入缓冲区中的所有字符或考虑不同fscanf
格式说明 符如何处理前导空格的人构成许多陷阱。
特别是,在读取num
的情况下,您无法使用%s
格式说明符限制fscanf
读取的字符数。 您不能包含可变字段宽度以防止写入超出数组边界。 (例如,您不能使用%nums
的%4s
来确保将读取的字符数限制为4
)当使用VLA根据从第一行读取的内容保存特定数量的字符时,根本就没有合并num
并validation/限制使用fscanf
读取的字符数的优雅方法。
如果在你的一条线的末端碰巧有一个杂散space
(或其他角色),所有这些都会导致等待发生的火车残骸 。
那么,如何处理只读4-char
进入square
和endsquare
每一行? ( 注意:首都'S'
被缩减为小写以匹配正常的C风格)当您需要处理输入行时,使用面向行的输入函数并提供足以处理每行数据的缓冲区。 我宁愿使用一个128-char
缓冲区,并确保我读取每4-5 char line
不是意外地将5-char
行读入4-char
缓冲区 – 以避免未定义的行为 。 此外,您可以使用相同的缓冲区来读取每一行数据。
接下来,您必须validation每次读取和每次转换,以确保您不会从代码中失败读取或转发失败的过程中处理垃圾。 例如,在读取数据文件时,可以声明一个简单的缓冲区并读取第一行,如下所示:
#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-chars
复制到square[i]
或endsquare[i]
。 由于您使用的是for
循环,因此您还必须检查您读取的每一行是否为有效行。 仅仅因为您将4
读作第一行,就无法保证文件中有8
行。 (理想情况下,您希望使用while (fgets (buf, MAX, in))
循环来驱动其余输入,并在读取4-lines
后使用计数器中断,但您可以通过validation来保护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
循环做同样的endsquare
。 总而言之,您可以执行以下操作( 注意: out
未使用,因此与其相关的代码将在下面注释掉):
#include #include #include #include #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; }
注意:永远不要使用variadic printf
函数输出单个字符,而是使用一个设计用于输出单个字符的函数,如putchar
(或fputc
)。 另请注意,如果在任何组中读取的行数少于(在您处),则应保留单个行的计数并将正确的数字传递给prints
。
示例输入
$ cat dat/list.txt 4 abcd efgh ijkl mnop qrst uvwx yz12 3456
示例使用/输出
$ ./bin/inout dat/list.txt abcd efgh ijkl mnop abcd efgh ijkl mnop qrst uvwx yz12 3456
您可以执行更多validation,但至少上述内容将适用于您的数据并进行合理的错误检查。 仔细看看,如果您有任何疑问,请告诉我。
如果要打印方块 ,可以始终执行以下操作:
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'); } }
示例w /修改 prints
$ ./bin/inout dat/list.txt abcd efgh ijkl mnop abcd efgh ijkl mnop qrst uvwx yz 1 2 3 4 5 6