简体   繁体   English

在不知道要输入的数量的情况下,逐行读取多个数组的输入

[英]Reading input for more than one array, line by line,without knowing the number of inputs to be given

I was given 3 arrays and the input for each array is given in a single line with space between each element. 给了我3个数组,每个数组的输入在一行中每个元素之间都有空格。

Example input: 输入示例:

3 2 1 1 1
4 3 2
1 1 4 1

So what I am trying to do is to assign all the elements of first line to array 1, second line to array 2 and third line to array 3. 所以我想做的是将第一行的所有元素分配给数组1,第二行将所有元素分配给数组2,第三行将所有元素分配给数组3。

#include <stdio.h>
int main()
{
    int a[20],b[20],c[20],d[3];
    int k=0;
    char temp;
    do{
        scanf("%d%c", &a[k], &temp); 
        k++; 
        } while(temp != '\n');
    d[0]=k;
    k=0;
    do{
        scanf("%d%c", &b[k], &temp); 
        k++; 
        } while(temp != '\n');
    d[1]=k;
    k=0;
    do{
        scanf("%d%c", &c[k], &temp); 
        k++; 
        } while(temp != '\n');
    d[2]=k;
    return 0;
}

This is what I tried, but this code saves all the elements in the first array itself. 这是我尝试过的方法,但是这段代码将所有元素保存在第一个数组本身中。 Any help? 有什么帮助吗?

I've just tried your code and it works fine - a, b, c are filled with the numbers entered via stdin. 我刚刚尝试了您的代码,它可以正常工作-a,b,c充满了通过stdin输入的数字。

However, your primary problem is that scanf is not line oriented. 但是,您的主要问题是scanf 不是面向行的。 You should instead use fgets to read the line in a string and parse it with strtok and sscanf . 您应该改用fgets读取字符串中的行,并使用strtoksscanf对其进行解析。

Taking the recommendation to use fgets is one thing, putting it into use the first time is quite another. 推荐使用fgets是一回事,第一次使用它是另一回事。 You use fgets (or POSIX getline ) because they provide a mechanism for reading an entire line of text into a buffer at once. 您使用fgets (或POSIX getline ),因为它们提供了一种将整个文本行一次读入缓冲区的机制。 This eliminates the pitfalls inherent in trying to use scanf for that purpose. 这消除了尝试为此目的使用scanf固有的陷阱。

While POSIX getline will handle a line of any length for you, it dynamically allocates storage for the resulting buffer. 尽管POSIX getline将为您处理任意长度的行,但它会动态地为结果缓冲区分配存储空间。 fgets on the other hand will read only as many characters as can be stored in the size you specify in the fgets call (reserving space for the nul-character , as fgets always provides a nul-terminated buffer) 另一方面, fgets只会读取您在fgets调用中指定的大小可以存储的尽可能多的字符(为nul-character保留空间,因为fgets始终提供以nul终止的缓冲区)

This means it is up to you to check that a complete line fit into the buffer you provided for fgets use. 这意味着您需要检查是否有完整的行适合您提供给fgets使用的缓冲区。 Essentially you want to check whether the buffer is full and the last character is not the '\\n' character. 本质上,您想检查缓冲区是否已满,并且最后一个字符不是'\\n'字符。 Note, you are not concerned with trimming the trailing newline here, just in checking for its presence to validate whether a complete line was read. 请注意,您不必担心在此处修剪尾随的换行符,而只是检查其是否存在以验证是否已读取完整的行。 So here you can check whether the length of buffer is your max size (minus 1 for the nul-character ) and the last character is not '\\n' . 因此,您可以在此处检查缓冲区的长度是否为最大大小( nul-character减去1 )并且最后一个字符不是'\\n' If those two conditions exist, you have no way of knowing whether the entire line was read (but see the note after this example). 如果存在这两个条件,则无法知道是否已读取整行(但请参见本示例后面的注释)。 A simple approach to the validation whether a full line was read into buf is, eg 验证是否将整行读入buf一种简单方法是,例如

    while (fgets (buf, MAXC, fp)) {
        ...
        size_t len = strlen (buf);  /* length for line validation */
        /* validate whole line read into buf - exit on error */
        if (len == MAXC - 1 && buf[len - 1] != '\n') {
            fprintf (stderr, "error: line %d too long.\n", row + 1);
            return 1;
        }

( note : for the corner-case of a file without a POSIX eof (end-of-file), eg without a '\\n' following the last line of text, there is a chance you could actually read an exact buffer full of characters and have no trailing '\\n' , but still have a complete read -- you can check for EOF with a call to getchar() and return the character to the buffer with putchar if it is other than EOF ) 注意 :对于没有POSIX eof (文件结尾)的文件的特殊情况,例如,在文本的最后一行之后没有'\\n'下,您实际上可能会读取一个完全为字符且没有尾随'\\n' ,但仍具有完整的读取-您可以通过调用getchar()来检查EOF ,如果不是EOF则可以使用putchar将字符返回到缓冲区中)

Now on to handling your arrays. 现在开始处理数组。 Rather than declaring separate arrays of 20 int each, instead declare a 2D array of n row of 20 int each. 与其声明每个20 int的独立数组,不如声明一个n20 int的2D数组。 This makes handling the read and indexing much easier. 这使得处理读取和索引变得更加容易。

You also have the problem of having to capture the number of values you store in each row. 您还存在必须捕获每行中存储的值数量的问题。 While you can do a little indexing magic and store the number of values in each row as the first-column value, it is probably a bit easier just to have a separate array of n values where each index corresponds to the number of values store for each row in your 2D array. 虽然您可以做一些索引魔术操作,并将每行中的值数存储为第一列值,但仅具有n值的单独数组(每个索引对应于要存储的值数)可能会容易一些2D阵列中的每一行。 For example, 例如,

    int row = 0,                    /* row count during read */
        idx[ROWS] = {0},            /* array holding col count per row */
        arr[ROWS][COLS] = {{0}};    /* 2D array holding each line array */

That way, each time you add a value to one of your rows, you simply increment the corresponding value in idx , eg 这样,每次向一个行中添加一个值时,您只需在idx增加相应的值即可,例如

            /* fill a value in row, then */
            idx[row]++;             /* update col-index for array */

With that background, you are finally ready to start filling your array. 在此背景下,您终于可以开始填充阵列了。 The approach is straight-forward. 该方法很简单。 You will: 你会:

  1. use an outer loop reading a complete line using fgets (buf, MAXC, fp) ; 使用外循环使用fgets (buf, MAXC, fp)读取fgets (buf, MAXC, fp)行;
  2. initialize inner loop variable (for offset , etc.); 初始化内部循环变量(用于offset等);
  3. check that a complete line was read (as shown above); 检查是否已读取完整行(如上所示);
  4. use an inner loop over buf using sscanf to repeatedly parse a single-integer from buf until all integers are read; buf使用sscanfbuf使用内部循环,反复从buf解析单整数,直到读取所有整数;
  5. (really 4(a.)) (you call sscanf on buf + offset from the beginning), saving the number characters consumed (saved with the %n format specifier to update offset ); (确实为4(a。))(您在buf + offset从头开始调用sscanf buf + offset ),保存消耗的字符数(用%n格式说明符保存以更新offset );
  6. update offset with the number of characters consumed, and repeat. 用消耗的字符数更新offset量,然后重复。

( note: it is up to you to protect your array bounds to make sure you do not attempt to store more integer values in each array than you have storage for, and that you do not try and store more rows than you have storage for. So on each the outer and inner loop you will add a check to limit the number of rows and columns you read to the available storage) 注意:您必须保护自己的数组边界,以确保在每个数组中存储的整数值不要超过存储的整数,并且存储的行数不要超过存储的整数。因此,在每个外部和内部循环上,您都将添加一个检查,以限制读取到可用存储的行和列的数量)

Your read loops implementing the steps above could look like the following: 您实现上述步骤的读取循环可能如下所示:

/* constants for max rows, cols, and chars for read buf */
enum { ROWS = 4, COLS = 20, MAXC = 512 };
...
    while (row < ROWS && fgets (buf, MAXC, fp)) {   /* read each line */
        int col = 0,        /* col being filled */
            nchr = 0,       /* no. chars consumed by sscanf */
            offset = 0,     /* offset in buf for next sscaf call */
            tmp = 0;        /* temp var to hold sscanf conversion */
        size_t len = strlen (buf);  /* length for line validation */
        /* validate whole line read into buf - exit on error */
        if (len == MAXC - 1 && buf[len - 1] != '\n') {
            fprintf (stderr, "error: line %d too long.\n", row + 1);
            return 1;
        }
        while (col < COLS &&    /* read each value in line into arr */
                sscanf (buf + offset, "%d%n", &tmp, &nchr) == 1) {
            arr[row][col++] = tmp;  /* assign tmp to array */
            offset += nchr;         /* update offset in buffer */
            idx[row]++;             /* update col-index for array */
        }
        row++;  /* increment row for next read */
    }

Putting it altogether, you could do something like the following: 综上所述,您可以执行以下操作:

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

/* constants for max rows, cols, and chars for read buf */
enum { ROWS = 4, COLS = 20, MAXC = 512 };

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

    int row = 0,                    /* row count during read */
        idx[ROWS] = {0},            /* array holding col count per row */
        arr[ROWS][COLS] = {{0}};    /* 2D array holding each line array */
    char buf[MAXC] = "";            /* buffer for fgets */
    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;
    }

    while (row < ROWS && fgets (buf, MAXC, fp)) {   /* read each line */
        int col = 0,        /* col being filled */
            nchr = 0,       /* no. chars consumed by sscanf */
            offset = 0,     /* offset in buf for next sscaf call */
            tmp = 0;        /* temp var to hold sscanf conversion */
        size_t len = strlen (buf);  /* length for line validation */
        /* validate whole line read into buf - exit on error */
        if (len == MAXC - 1 && buf[len - 1] != '\n') {
            fprintf (stderr, "error: line %d too long.\n", row + 1);
            return 1;
        }
        while (col < COLS &&    /* read each value in line into arr */
                sscanf (buf + offset, "%d%n", &tmp, &nchr) == 1) {
            arr[row][col++] = tmp;  /* assign tmp to array */
            offset += nchr;         /* update offset in buffer */
            idx[row]++;             /* update col-index for array */
        }
        row++;  /* increment row for next read */
    }
    if (fp != stdin) fclose (fp);   /* close file if not stdin */

    for (int i = 0; i < row; i++) { /* output the arrays read */
        for (int j = 0; j < idx[i]; j++)
            printf (" %3d", arr[i][j]);
        putchar ('\n');
    }

    return 0;
}

Note: rather than using a fixed size 2D array, you can take things a step further and instead use a pointer-to-pointer-to-int (eg a double-pointer, int **arr; ) and dynamically allocate and reallocate pointers for rows, as required, and dynamically allocate and reallocate the storage assigned to each pointer to handle any number of integer values per-row. 注意:除了使用固定大小的2D数组,您可以更进一步,而可以使用指针到指针的整数 (例如,双指针, int **arr; )并动态分配和重新分配指针对于行,根据需要,并动态分配和重新分配分配给每个指针的存储,以处理每行任意数量的整数值。 While it is not that much additional work, that is left as an exercise to you when you get to dynamic allocation in your studies. 尽管没有太多额外的工作,但是当您在学习中进行动态分配时,这只是练习。 What you are doing with an differing number of column values per-row is creating a jagged array . 每行具有不同数量的列值的操作是创建一个锯齿状的数组

Example Input File 输入文件示例

Using your input file for testing, eg: 使用您的输入文件进行测试,例如:

$ cat dat/3arr.txt
3 2 1 1 1
4 3 2
1 1 4 1

Example Use/Output 使用/输出示例

Produces the following output: 产生以下输出:

$ ./bin/arr_jagged dat/3arr.txt
   3   2   1   1   1
   4   3   2
   1   1   4   1

Look things over and let me know if you have further questions. 仔细检查一下,如果您还有其他问题,请告诉我。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM