使用fscanf填充一个char数组会更改另一个char数组的值 [英] Populating one char array using fscanf changes the value of another char array

查看:57
本文介绍了使用fscanf填充一个char数组会更改另一个char数组的值的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我首先使用fscanf填充我的第一个数组,然后再次使用同一输入文件中的fscanf填充另一个数组。但是,这似乎正在移动我的第一个数组中的值。

I'm first using fscanf to populate my first array then again using fscanf from the same input file to populate another array. However this seems to be shifting the values in my first array.

这是我的输入内容:

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之后,我的方阵似乎已更改。

As you can see my square array seems to be changed after I populate endSquare.

推荐答案

除了不考虑%s 格式说明符将添加的 nul-termination 字符外,您还会通过尝试使事情变得非常困难使用格式化的输入函数 fscanf 读取数据。在进行面向行的输入时,最好使用面向行的输入函数,例如 fgets 和然后从包含整行信息的缓冲区中解析所需的信息。为什么?将数字和字符输入与 scanf 函数系列混合使用会给那些无法解决输入缓冲区中所有字符的用户带来很多陷阱或说明不同的 fscanf 格式说明符如何处理领先的空白

In addition to not accounting for the nul-terminating character that the %s format specifier will append, you are making things extremely hard on yourself by attempting to read lines of data with a formatted input function fscanf. When doing line-oriented input, it is far better to use a line-oriented input function such as fgets and then parse the needed information from the buffer containing the entire line of information. Why? Mixing numeric and character input with the scanf family of functions can pose many traps for those who do not account for all characters that remain in the input buffer or account for how the differing fscanf format specifiers handle leading whitespace.

特别是,在您读取 num 的情况下,您没有办法限制 fscanf 使用%s 格式说明符。您不能包含变量 field width 来防止超出数组范围的写入。 (例如,您不能%4s 使用类似%nums 的东西来确保您将读取的字符限制为 4 )当使用VLA根据从第一行读取的内容来容纳特定数量的字符时,没有一种优雅的方法合并 num 并使用 fscanf 验证/限制读取的字符数。

Particularly, in your case when reading num, you have no way to limit the number of characters read by fscanf using the %s format specifier. You cannot include a variable field width to protect against writing beyond your array bounds. (e.g. you can't use something like %nums for %4s to insure you limit the characters read to 4) When using a VLA to hold a specific number of characters based on what is read from the first line, there just isn't an elegant way to incorporate num and validate/limit the number of characters read using fscanf.

如果碰巧有一个空格(或其他字符),所有这些加在一起会导致火车残骸 )放在一行的结尾处。

All of this adds up to a train-wreck waiting to happen if there happens to be a stray space (or other character) at the end of one of your lines.

因此,如何处理仅将 4-char 读入每一行 square endsquare 的行? (注意:,将大写字母'S'减小为小写字母以匹配普通的C风格)。当您需要处理输入行时,使用面向行的输入函数,并提供足以处理每一行数据的缓冲区。我宁愿使用 128个字符缓冲区并确保我读取每个 4-5个字符行,而不是意外读取<将code> 5-char 行插入 4-char 缓冲区中-以避免未定义行为。此外,您可以使用相同的缓冲区读取每一行数据。

So, how to handle reading only 4-char into each row of square and endsquare? (note: the capital 'S' was reduced to lower-case to match normal C-style) When you need to handle lines of input, use a line oriented input function and provide a buffer sufficient to handle each line of data. I would rather use a 128-char buffer and insure I read every 4-5 char line than accidentally read a 5-char line into a 4-char buffer -- to avoid Undefined Behavior. Moreover, you can use the same buffer to read every line of data.

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

Next, you must validate every read and every conversion to insure you do not process garbage from the point of the failed read or failed conversion forward in your code. For example, when reading your data file, you can declare a simple buffer and read your first line as follows:

#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;
    }

在阅读后续各行时,需要确认 entre -line 是通过检查结尾的'\n'来读取的(由 fgets 读取并包含在其中) ),如果该行不存在,则您的行太长(句柄错误)。您还需要知道行中是否有 num 个字符,因为您没有将行存储为字符串(仅作为字符数组存储)。如果没有读取 num-chars ,则不能将 num-chars 复制到 square [ i] endsquare [i] 。由于您正在使用 for 循环,因此还必须检查读取的每一行都是有效行。仅仅因为您将 4 读为第一行,所以不能保证文件中有 8 行。 (理想情况下,您希望使用 while(fgets(buf,MAX,in))循环来驱动其余输入,并在<$ c $之后使用计数器来中断读取了c> 4行,但是您可以在 for 循环中使用 if(fgets( buf,MAX,in))

When reading each subsequent line, you need to confirm that the entire-line was read by checking for the trailing '\n' (read and included by fgets) and if it isn't present, your line is too long (handle error). You also need to know if there are num characters in the line since you are NOT storing the lines as strings (only as a character array). If there are not num-chars read, you cannot copy num-chars to square[i] or endsquare[i]. Since you are using a for loop, you also must check that each line you read is a valid line. Just because you read 4 as line one, there is no guarantee there are 8 lines in the file. (you would ideally want to use a while (fgets (buf, MAX, in)) loop to drive the rest of the input and a counter to break after 4-lines are read, but you can protect within the for loop by validating with an if (fgets (buf, MAX, in)) as well.

考虑到这一点,您可以用完全 4个字符(或更少),同时仍然使用 for 循环检查类似以下内容的意外空行:

With that in mind, you could fill the character arrays with exactly 4-chars read from the file (or less) while checking for any unexpected empty lines with something like the following while still using your for loop:

    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 未使用,因此与之相关的代码在下面注释掉) :

You can do the same for your endsquare loop. Putting it all together, you could do something like the following (note: out is unused, so the code related to it is commented out below):

#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 函数可输出单个字符,而应使用设计为输出单个字符的函数,例如 putchar (或 fputc )。另请注意,如果少于 num prints $ c>行可在任何组中读取(留给您使用)。

Note: never use the variadic printf function to output a single-character, instead, use a function designed to output a single character like putchar (or fputc). And also note, you should preserve the count of individual lines and pass the correct number to prints in the event there are less than num lines read in any group (that is left for you).

示例输入

$ 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

总是可以进行更多的验证,但是在至少上述类似的东西将通过合理的错误检查为您的数据工作。仔细检查一下,让我知道是否有任何疑问。

There are always more validations you can do, but at a minimum something like the above will work for your data with reasonable error checking. Look things over and let me know if you have any questions.

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

If you want to print squares, you could always do something like the following:

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');
    }
}

示例(已修改) 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

这篇关于使用fscanf填充一个char数组会更改另一个char数组的值的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆