繁体   English   中英

如何在没有空格的情况下读取未知长度的矩阵?

[英]How do I read in a matrix of unknown length, without whitespaces?

我的输入文件如下所示:

1,12,5,14,0
4,6,2,3,24
1,2,3,4,5

每条线都有相同的长度,但没有指定有多少条线,以及它们有多长。 (最大尺寸为 20*20)。

我需要将它们读入一个 20*20 的数组,以便如果该行只有 2 个数字长,则数组的其余部分为空。

当文件中出现换行符时,我该怎么做,同时省略,字符并从数组的下一行开始?

这是我的尝试:

int matrix[20][20];

for (int i=0; i<19; i++){
    for (int j=0; j<19; j++){
        fscanf(be, "%d,", &matrix[i][j]);
    }
    fscanf(be, "\n");
}

Ps:我只允许使用<stdio.h><stdlib.h>

我有这个想法。 下面的代码为每个循环周期从文件中读取一行并扫描它,如果它不是空行。 使用函数(在代码中实现) mystrchr检索分隔符SEP

#include <stdio.h>
#include <stdlib.h>

char * mystrchr(const char * s, char x);

char * mystrchr(const char * s, char x) 
{
    while(*s!=0 && *s!=x) s++;

    return ((!*s)?NULL:(char *)s);
}

int main(void)
{
#define FILENAME "x.txt"
#define ROWS 20
#define MAXEL 20
#define BDIM 10240
#define SEP ','

    int x[ROWS][MAXEL],elr[ROWS], ronum=0,elnum,i,j;
    FILE * fptr;
    char buffer[BDIM],* app;

    /* --- Init the array --- */
    for(i=0;i<ROWS; i++)
        for(j=0;j<MAXEL; j++)
            x[i][j]=0;

    /* --- Open the file --- */
    fptr=fopen(FILENAME,"r");
    if (!fptr)
        return 1;

    /* --- Gets data from file --- */
    while(ronum<ROWS && fgets(buffer,BDIM,fptr)) {
        app = buffer;
        /* Takes only non-void lines */
        if (*app && *app!='\n' && *app!='\r') {
            elnum=0;--app;
            /* Scan the line */
            do {
                if (* ++app ) {
                    x[ronum][elnum++]=strtol(app,NULL,0);
                }

                app = mystrchr(app,SEP);
            } while(app && elnum<MAXEL);

            // -- save the number of element per row
            if (elnum) {
                elr[ronum]=elnum;
                ronum++;
            }
        }
    }


    if (fptr)
        fclose(fptr);

    /* --- Prints data --- */
    for(i=0;i<ronum;i++) {
        for(j=0;j<elr[i];j++) {
            printf("%d ",x[i][j]);
        }
        puts("");
    }

    return 0;
}

可以使用fgetc逐个字符读取未指定长度的行。 在您的情况下,您必须检查每个字符是否符合以下条件:

  • 如果你看到一个',' ,你就看到了一个数字;

  • 如果你看到一个'\\n' ,你就看到了一个数字你看到了一条线。

  • 您跳过每个非数字字符;

  • 您将数字放在一个小的临时缓冲区中。 一旦你看到一个数字,你可以使用atoi转换它;

  • 如果不允许使用atoi ,则执行number= number*10 + c-'0'; c表示您看到的每个数字的数字。

从描述中不清楚一行是否可能为空。

假设所有行都是非空的,那么这是一个非常简单的scanf()工作,但重要的一点是永远不要丢弃输入函数的返回值

几种可能的方法:

  • 使用%d模式扫描每行的第一个数字(返回值 0 表示我们已读取所有行),然后使用,%d扫描该行的后续数字。 当输入流中没有,时, scanf()将返回 0,然后我们移至下一行。
  • 使用%d%c模式进行扫描以将数字后面的字符捕获到变量中(例如char sep )。 当返回值为 0 时,我们已读取所有行,并中断循环。 当返回值为 2 时,我们可以检查sep看它是,还是\\n并选择是否前进到下一行。 (如果返回值为 1,则最后一个数字在文件末尾,没有最后的换行符)。

如果某些或所有行可能为空,您将需要查看该行的第一个字符是否为换行符(可能使用getc()ungetc() )。 那将指示一个空行。

实际上你只需要<stdio.h> 您可以通过使用fgets()每一行读入缓冲区来分隔值。 您可以使用sscanf解析缓冲区中的每个值,并使用"%n"说明符维护缓冲区开头的偏移量,以确定每次转换为int期间消耗的字符数。 您将该数字( +1以考虑',' )添加到每个转换值的偏移量中。

注意:理想情况下,给定stdlib.h您可能希望使用strtol来处理转换,因为它提供了完整的错误处理,允许您确定转换失败的原因并处理失败,但没有限制limits.herrno.h您无法确定转换过程中是否发生下溢/溢出(不提供您自己的LONG_MIN/LONG_MAX值),因此您只能检查字符是否已转换——您可以使用sscanf

在你的情况下,首先#define常量为您的行,列和缓冲区的大小,例如数

#include <stdio.h>

#define ROWS 20     /* if you need a constant, #define one (or more) */
#define COLS ROWS
#define MAXC 1024

现在声明您的数组和缓冲区以及行和列变量,打开指定为程序第一个参数的文件名并验证它是否可以读取(如果没有给出参数,则默认从stdin读取):

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

    char buf[MAXC] = "";    /* buffer to hold each line read */
    int arr[ROWS][COLS] = {{0}}, row = 0, col = 0;  /* array & bounds */
    /* use filename provided as 1st argument (stdin by default) */
    FILE *fp = argc > 1 ? fopen (argv[1], "r") : stdin;

    if (!fp) {  /* validate file open for reading */
        perror ("file open failed");
        return 1;
    }

现在将文件中的每一行读入buf并声明您将用于跟踪从buf开头的offset量、每次转换期间消耗的字符数n 、行中的列数ncol和解析的值val的变量:

    while (row < ROWS && fgets (buf, MAXC, fp)) {     /* read each line */
        int offset = 0,     /* offset from beginning of line */
            n = 0,          /* number of characters consumed in conversion */
            ncol = 0,       /* number of columns in line */
            val;            /* value from conversion */

现在循环维护您的偏移量并转换行中的每个值,将其添加到您的数组中:

        while (ncol < COLS && sscanf (buf + offset, "%d%n", &val, &n) == 1) {
            arr[row][ncol++] = val; /* assign val, increment ncol */
            offset += n + 1;        /* update offset + 1 for ',' */
        }

现在一些家务。 如果需要确保数组中的每一行都有相同的列数,请根据第一行解析的值数设置列数。 然后对于所有其他行,与该值进行比较以确保每行具有相同数量的值:

        if (!col)           /* if number of columns not set */
            col = ncol;     /* set to no. columns in 1st row */
        if (ncol != col) {  /* force all other rows to have same no. cols */
            fputs ("error: unequal number of columns.\n", stderr);
            return 1;
        }

解析所有值后,只需增加行计数器并转换下一行值:

        row++;      /* increment row count */
    }

从本质上讲,这就是您需要做的所有事情。 剩下的就是关闭文件流(如果不是从stdin读取)并根据需要使用数组中的值。 一个完整的例子可能是:

#include <stdio.h>

#define ROWS 20     /* if you need a constant, #define one (or more) */
#define COLS ROWS
#define MAXC 1024

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

    char buf[MAXC] = "";    /* buffer to hold each line read */
    int arr[ROWS][COLS] = {{0}}, row = 0, col = 0;  /* array & bounds */
    /* use filename provided as 1st argument (stdin by default) */
    FILE *fp = argc > 1 ? fopen (argv[1], "r") : stdin;

    if (!fp) {  /* validate file open for reading */
        perror ("file open failed");
        return 1;
    }

    while (row < ROWS && fgets (buf, MAXC, fp)) {     /* read each line */
        int offset = 0,     /* offset from beginning of line */
            n = 0,          /* number of characters consumed in conversion */
            ncol = 0,       /* number of columns in line */
            val;            /* value from conversion */
        /* loop over each value in line */
        while (ncol < COLS && sscanf (buf + offset, "%d%n", &val, &n) == 1) {
            arr[row][ncol++] = val; /* assign val, increment ncol */
            offset += n + 1;        /* update offset + 1 for ',' */
        }
        if (!col)           /* if number of columns not set */
            col = ncol;     /* set to no. columns in 1st row */
        if (ncol != col) {  /* force all other rows to have same no. cols */
            fputs ("error: unequal number of columns.\n", stderr);
            return 1;
        }
        row++;      /* increment row count */
    }

    if (fp != stdin)   /* close file if not stdin */
        fclose (fp);

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

该示例仅输出存储在数组中的值并退出。

示例输入文件

$ cat dat/2020max.txt
1,12,5,14,0
4,6,2,3,24
1,2,3,4,5

示例使用/输出

$ ./bin/read2020max dat/2020max.txt
   1  12   5  14   0
   4   6   2   3  24
   1   2   3   4   5

仔细检查一下,如果您还有其他问题,请告诉我。

假设数据格式正确,用fgets()读取一行,然后通过strtol()解析它

#define COL_MAX 20
#define ROW_MAX 20
#define CHAR_PER_INTEGER (sizeof(long)*CHAR_BIT/3 + 3)
#define LINE_MAX (ROW_MAX * (CHAR_PER_INTEGER + 1) + 2)

long matrix[ROW_MAX][COL_MAX] = {0};  // better to use named constants than magic numbers

int col = 0; 
int row; 
// for (int i=0; i<19; i++){  Not to 19, but 20
for (row=0; row<ROW_MAX; row++){
  char line[LINE_MAX*2]; // Generous buffer, but not insane size
  if (fgets(line, sizeof line, be) == NULL)) {
    break; // No more lines
  }
  char *s = line;
  for (int j=0; j<COL_MAX; j++){
    char *endptr;
    errno = 0;
    long value = strtol(s, &endptr, 10);
    // no conversion, overflow, unexpected next character
    if (s == endptr || errno || (*endptr != ',' && *endptr != '\n')) {
      fprintf(stderr, "Invalid input <%s>\n", line);
      exit(EXIT_FAILURE);
    }
    matrix[row][j] = value;
    if (*endptr == '\n') {  // no more in this line
      col = j + 1;
      break;
    }
    s = endptr + 1; // advance past the ','
  }
}

// Use row*col portion of the matrix.

暂无
暂无

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

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