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

标签 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之后,我的平方数组似乎发生了变化。

最佳答案

除了不考虑%s格式说明符将附加的num终止字符外,您还试图使用格式化输入函数fscanf读取数据行,这使您的工作变得非常困难在进行面向行的输入时,最好使用一个面向行的输入函数,比如fgets,然后从包含整行信息的缓冲区中解析所需的信息为什么?将数字和字符输入与scanf函数族混合可以为那些不考虑在输入缓冲区中保留的所有字符或为不同的fscanf格式说明符如何处理引导空白的帐户提供许多陷阱。
特别是,在您读取num时,无法使用fscanf格式说明符限制%s读取的字符数不能包含可变字段宽度以防止写入超出数组边界(例如,您不能使用%numsfor%4s之类的东西来确保将读取的字符限制为4)当使用VLA根据从第一行读取的内容来保存特定数量的字符时,没有一种优雅的方法来合并num和验证/限制使用fscanf读取的字符数。
所有这些加起来就构成了一个火车事故,如果你的一条线路的尽头碰巧有一个流浪的space(或其他字符)的话,那么它就要发生了。
那么,如何处理对4-charsquare的每一行的只读(注意:大写endsquare被简化为小写以匹配普通的C样式)当需要处理输入行时,使用面向行的输入函数并提供足够的缓冲区来处理每行数据为了避免未定义的行为,我宁愿使用'S'缓冲区并确保读取每个128-char而不是意外地将4-5 char line行读入5-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;
    }

读取每个后续行时,需要通过检查尾随的4-char(read and included by'\n')来确认整行已被读取,如果不存在,则说明行太长(handle error)您还需要知道行中是否有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))循环:
    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 */
    }

您可以对4-chars循环执行同样的操作把所有这些放在一起,您可以执行如下操作(注意:for未使用,因此与之相关的代码在下面被注释掉):
#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;
}

注意:不要使用variadicendsquare函数来输出单个字符,而是使用设计用于输出单个字符的函数,如out(或printf)还要注意的是,您应该保留单个行的计数,并将正确的数字传递给putchar,以防在任何组(留给您的)中读取的行数少于fputc
示例输入
$ 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

总是有更多的验证您可以做,但至少像上面这样的东西将工作与合理的错误检查您的数据仔细看一下,如果有什么问题请告诉我。
如果要打印正方形,可以执行以下操作:
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
a b c d
e f g h
i j k l
m n o p

a b c d
e f g h
i j k l
m n o p

q r s t
u v w x
y z 1 2
3 4 5 6

关于c - 使用fscanf填充一个char数组会更改另一个char数组的值,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/43964879/

相关文章:

arrays - 反向过滤数组 Angular

C++ - 转换 float 组的 std::string

c - 打印未初始化数组的 atoi 值

c - perlbench 在 SPEC 2006 线束之外导致段错误

c - 从链表中删除元素

c++ - 在套接字 send() 和 receive() 上使用超时

c++ - 对 GCC 使用 AT&T 内联汇编器

javascript - 从数组 : pop is not a function (javascript) 中删除元素

Java通过3类进行排序

c - 如何将一个宏的结果传递给另一个宏?