【问题标题】:Reading Matrix from text file into 2D array using double pointer of struct in C programming [closed]在C编程中使用结构的双指针将矩阵从文本文件读入二维数组[关闭]
【发布时间】:2016-07-29 20:07:17
【问题描述】:

我需要将文本文件中的一些数据存储到变量中。这些变量给了我矩阵的大小。现在我需要从文件中读取矩阵并存储它,然后使用结构的双指针来指向矩阵。到目前为止,这是我的代码:

我正在使用的结构 -

struct data{
        char str[20];
        int row, col;
        int **arr;
};

将需要的信息提取到变量中-

f = fopen("input.txt","r");
        fscanf(f,"%d",&num);
        printf("%d\n",num);
        fgets(NULL,NULL,f);
        fscanf(f,"%s %d %d",ptr->str,&(ptr->row),&(ptr->col));

现在使用行和列值,我需要从同一个文件中读取一个矩阵,将此信息存储到结构的双指针中。

类似这样的:

m = (int **)malloc((ptr->row)*(ptr->col)*(sizeof(int)));

 for(i=0;i<ptr->row;i++){
                for(j=0;j<ptr->col;j++){
                        fscanf(f,"%d",&m[i][j]);
                }
        }

但最后一部分会引发 Seg Fault。请帮忙 - 我应该怎么做?

作为参考,我也分享了矩阵:

50                                                                               
BOROVETS 58 24                                                                                                                    
1090 1091 1060 1137 542 52 768 721 620 737 108 660 1069 789 356 1361 1255 1014 1171 494 57 565 122 1252
1052 1118 17 490 594 22 1375 1070 571 236 82 87 1170 266 873 1340 529 1164 129 700 255 1041 972 927
388 1324 420 1077 8 237 991 181 782 531 94 248 676 1322 498 480 867 816 743 932 701 167 551 569
192 992 588 933 381 467 667 900 1072 670 509 1027 485 644 62 1264 432 400 26 357 453 1260 998 1244
530 805 1360 843 37 1257 144 196 137 749 1154 575 96 206 164 466 1381 703 746 112 549 760 148 621
1138 1219 1005 977 308 1057 303 367 518 326 333 817 354 31 353 1022 136 421 305 1348 652 208 846 320
395 1291 1332 1315 653 872 1097 1007 340 1231 517 745 1008 732 631 1355 501 835 1100 445 514 987 636 578
619 882 880 439 327 437 1127 361 662 1243 163 965 275 1076 832 55 207 787 952 1073 76 109 335 1028
964 103 185 80 435 1305 694 339 1034 413 239 1001 626 1321 169 1325 1282 97 1262 906 212 974 554 1123
1247 566 1237 41 214 996 287 1298 240 256 1012 1035 829 1049 960 1223 416 1018 64 45 706 90 959 175
674 1283 491 1365 277 753 1277 1232 43 546 1011 771 1006 282 792 601 813 288 683 707 162 2 651 673
699 46 604 779 291 436 125 1009 1272 1085 300 1238 343 482 1165 844 671 623 661 1301 1048 550 213 155
610 486 794 797 727 1167 319 950 1046 1186 985 1044 1285 1230 202 831 858 449 143 856 1194 49 724 754
1126 177 119 830 35 1183 456 193 1039 1199 695 450 687 731 114 635 1019 864 1320 800 158 1284 630 1056
1088 562 88 625 1188 161 313 845 176 1016 198 1225 1302 924 634 532 165 1051 417 711 696 1334 465 847
886 639 1117 1066 643 130 51 819 1376 1113 166 265 390 903 702 999 228 492 284 411 424 712 572 1161
1236 75 369 1354 329 124 545 1368 274 1198 231 1300 234 896 306 138 1248 561 951 919 814 519 1271 573
222 1202 146 1349 159 512 127 377 1326 934 1101 358 1136 123 295 668 1344 1130 570 430 48 1196 199 261
1058 92 940 324 243 1265 663 547 344 877 302 1295 1218 778 1313 1319 1156 586 962 259 387 201 1251 785
656 540 593 1205 487 1387 504 328 1182 497 444 680 1105 576 496 60 1329 1120 790 865 69 1312 227 1353
755 1352 405 460 911 516 1042 1178 897 1204 714 29 1241 1083 475 1184 341 331 684 378 1346 1333 351 622
1169 596 120 1020 271 1061 837 270 1158 763 1038 581 920 1110 468 1 318 1155 675 534 178 657 1121 693
642 1145 1108 471 376 230 401 654 1122 502 564 995 1369 851 1089 218 928 579 267 1372 577 898 1309 440
310 1364 14 741 557 438 1279 479 483 781 1166 422 993 836 734 50 904 1287 1356 463 1374 511 807 472
1115 759 101 116 777 406 1017 527 398 1197 801 567 1337 451 747 382 1098 285 1036 1033 476 1246 713 407
393 1125 1114 1040 946 89 1071 98 1210 975 191 370 402 535 296 6 104 442 1245 197 1224 186 769 853
1086 1132 1268 583 355 312 1193 915 322 976 332 447 1030 947 78 891 828 86 190 350 820 25 30 607
875 690 56 935 1131 263 936 825 1010 1384 366 172 131 1208 1207 1037 892 412 1308 1149 742 1373 1151 189
861 1103 105 1358 883 258 1133 379 614 728 0 457 740 141 884 1024 1045 180 42 464 292 520 705 253
942 608 147 826 1292 780 893 1386 744 918 276 1054 669 1294 598 590 1217 171 1234 718 618 39 716 446
93 524 1229 666 840 943 1281 961 1259 102 151 522 1013 368 874 1233 1314 301 723 428 528 704 113 1391
1389 708 1253 692 286 563 1226 63 1082 914 523 209 1227 821 855 425 772 1065 1239 994 862 224 3 1109
1296 633 394 1104 839 1190 863 1214 810 617 273 1317 383 793 429 1177 1328 246 1299 374 1240 1135 12 15
905 99 868 941 591 1330 1080 812 1341 414 795 537 229 7 988 548 20 58 294 913 956 269 293 28
215 469 154 484 990 1141 1310 605 200 1107 1275 1383 1116 1367 859 930 188 854 556 1021 216 316 733 79
632 730 245 38 389 543 658 187 182 1152 916 597 580 362 1179 1078 1163 1345 899 602 1363 72 1180 973
560 1216 1258 922 1335 204 852 47 871 773 766 890 1191 419 1267 908 452 330 526 194 309 681 729 325
848 822 338 929 1362 115 750 346 409 1084 878 1318 1025 1134 1303 226 627 957 616 24 650 289 232 392
184 170 1029 365 1278 665 363 1359 1338 272 348 66 149 1128 726 499 738 211 40 841 168 1159 1095 126

58 和 24 是 ROW 和 COLUMN 值。

【问题讨论】:

  • int ** 不是二维数组(又名矩阵)。它也不能代表一,不指向一。如果您需要二维数组,请使用二维数组
  • 二维数组在哪里?问题标题中提到了它,但在正文和代码中都没有提到。 ptr 是什么,它在哪里初始化?
  • fgets(NULL,NULL,f); 充其量是未定义的行为。你想达到什么目的? 50 代表什么?
  • @Olaf 和 user3078414 :如果我执行 malloc 并尝试以二维数组方式存储数据。不是一样的吗? m = (int **)malloc((ptr->row)*(ptr->col)*(sizeof(int)));我试图用这部分代码来达到同样的效果。
  • @Bob__ :这只是移动到下一行。 fscanf 不会将文件指针移动到下一行的事实。我使用 fgets 只是为了移到文件的第二行。 50代表矩阵的数量。该文件中有 50 个矩阵。

标签: c matrix multidimensional-array file-io double-pointer


【解决方案1】:

如果您验证每个分配和每次读取,您会发现您发布的数据仅适用于 3824 整数。

您发布的代码中存在许多问题。但是,最重要的错误是代码中的每个关键步骤都缺乏验证。如果任何值被用作输入(无论是由用户输入,还是从文件中读取)并稍后在您的代码中使用,您必须验证您是否实际收到了您期望的数据。在处理任何输入错误时,您还可以选择是在错误时退出还是发出警告并用默认值替换它的位置(例如 nul 或零)。

在声明要填充的结构实例时,初始化值是有意义的(至少是第一个成员,以便将其余部分初始化为 0/nul)。

从打开文件开始,您不想从文件中读取,除非并且直到您确认它已打开以供读取,例如

    FILE *fp = fopen (argv[1], "r");

    if (!fp) {  /* validate file open for reading */
        fprintf (stderr, "error: file open failed '%s'.\n", argv[1]);
        return 1;
    }

然后在处理文件中的初始行时,最好将值读入缓冲区,然后根据需要丢弃或解析缓冲区。只需要一些简单的东西:

#define BUFSZ 256
...
    char buf[BUFSZ] = "";
    ...
    if (!fgets (buf, BUFSZ, fp)) {  /* throw away 1st line */
        fprintf (stderr, "error: empty file.\n");
        return 1;
    }

    if (!fgets (buf, BUFSZ, fp)) {  /* read 2nd line - str, row, col */
        fprintf (stderr, "error: reading data information.\n");
        return 1;
    }

    /* parse str, row, col & validate */
    if (sscanf (buf, " %19s %d %d", brovets.str, &brovets.row, &brovets.col)
               != 3) {
        fprintf (stderr, "error: unable to parse data information.\n");
        return 1;
    }

由于您希望保存 integer 值的成员是 pointer-to-pointer-to-int,因此您需要声明 row 指针数,您将分配并分配给每个内存块,足以容纳col 数量的ints。

您还可以在malloccalloc 之间进行选择。使用calloc 进行分配(既分配又初始化为0/NULL)都有一定的优势。例如,如果分配58 指针,初始化为NULL 并且只读取38 行的数据,则可以遍历有效指针以访问您拥有的数据,除了保留一个计数器来记录填充的行数之外,还有另一种方法。例如:

    /* allocate row number of pointers - initialize to zero w/calloc */
    if (!(brovets.arr = calloc (brovets.row, sizeof *brovets.arr))) {
        fprintf (stderr, "error: row pointer allocation failed.");
        return 1;
    }

    for (int i = 0; i < brovets.row; i++) { /* for each row */
        /* allocate storage for col ints & validate alloation */
        if (!(brovets.arr[i] = calloc (brovets.col, sizeof **brovets.arr))) {
            fprintf (stderr, "error: memory exhausted, row %d.\n", i);
            return 1;
        }
        for (int j = 0; j < brovets.col; j++)   /* read each value, validate */
            if (fscanf (fp, " %d", &brovets.arr[i][j]) != 1) {
                fprintf (stderr, "error: read of arr[%d][%d]\n", i, j);
                free (brovets.arr[i]);  /* free current row */
                brovets.arr[i] = NULL;  /* reset pointer for row to NULL */
                goto datadone;          /* leave input loop */
            }
    }
    datadone:;
    if (fp != stdin) fclose (fp);       /* close file if not stdin */

    printf ("\n name: %s\n rows: %d\n cols: %d\n\n",  /* output name, row, col */
            brovets.str, brovets.row, brovets.col);

    for (int i = 0; brovets.arr[i]; i++) {     /* output values stored in arr */
        for (int j = 0; j < brovets.col; j++) printf (" %4d", brovets.arr[i][j]);
        putchar ('\n');
    }

最后,您需要跟踪并释放您分配的所有内存。为此,您可以为分配的每个块保留一个指向起始地址的指针。这里是初始化你的行指针NULL可以帮助告诉你需要哪些行free,你可以遍历有效指针直到你的第一个@987654339达到@(或保留一个计数器)。

    /* free allocated memory */
    for (int i = 0; brovets.arr[i]; i++) free (brovets.arr[i]);
    free (brovets.arr);

总而言之,(并允许从指定为第一个参数的文件中输入,或默认为stdin)您可以按以下方式编写代码,并确保您只存储完整的数据行,并处理任何以理智的方式出错,并在完成后释放所有分配的内存。

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

#define BUFSZ 256

struct data {
    char str[20];
    int row, col;
    int **arr;
};

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

    char buf[BUFSZ] = "";
    struct data brovets = { .str="" };
    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;
    }

    if (!fgets (buf, BUFSZ, fp)) {  /* throw away 1st line */
        fprintf (stderr, "error: empty file.\n");
        return 1;
    }

    if (!fgets (buf, BUFSZ, fp)) {  /* read 2nd line - str, row, col */
        fprintf (stderr, "error: reading data information.\n");
        return 1;
    }

    /* parse str, row, col & validate */
    if (sscanf (buf, " %19s %d %d", brovets.str, &brovets.row, &brovets.col)
                != 3) {
        fprintf (stderr, "error: unable to parse data information.\n");
        return 1;
    }

    /* allocate row number of pointers - initialize to zero w/calloc */
    if (!(brovets.arr = calloc (brovets.row, sizeof *brovets.arr))) {
        fprintf (stderr, "error: row pointer allocation failed.");
        return 1;
    }

    for (int i = 0; i < brovets.row; i++) { /* for each row */
        /* allocate storage for col ints & validate alloation */
        if (!(brovets.arr[i] = calloc (brovets.col, sizeof **brovets.arr))) {
            fprintf (stderr, "error: memory exhausted, row %d.\n", i);
            return 1;
        }
        for (int j = 0; j < brovets.col; j++)   /* read each value, validate */
            if (fscanf (fp, " %d", &brovets.arr[i][j]) != 1) {
                fprintf (stderr, "error: read of arr[%d][%d]\n", i, j);
                free (brovets.arr[i]);  /* free current row */
                brovets.arr[i] = NULL;  /* reset pointer for row to NULL */
                goto datadone;          /* leave input loop */
            }
    }
    datadone:;
    if (fp != stdin) fclose (fp);       /* close file if not stdin */

    printf ("\n name: %s\n rows: %d\n cols: %d\n\n",  /* output name, row, col */
            brovets.str, brovets.row, brovets.col);

    for (int i = 0; brovets.arr[i]; i++) {     /* output values stored in arr */
        for (int j = 0; j < brovets.col; j++)
            printf (" %4d", brovets.arr[i][j]);
        putchar ('\n');
    }

    /* free allocated memory */
    for (int i = 0; brovets.arr[i]; i++) free (brovets.arr[i]);
    free (brovets.arr);

    return 0;
}

使用/输出示例

$ ./bin/array_brovets <dat/brovets.txt
error: read of arr[39][0]

 name: BOROVETS
 rows: 58
 cols: 24

 1090 1091 1060 1137  542   52  768  721  620  737  108  660 1069  789  356 1361 1255 1014 1171  494   57  565  122 1252
 1052 1118   17  490  594   22 1375 1070  571  236   82   87 1170  266  873 1340  529 1164  129  700  255 1041  972  927

您需要验证另外一个问题——您正确使用并释放了您分配的所有内存。 Linux 上的valgrind 是正常选择,但每个平台都有类似的内存检查器。例如

内存错误检查

$ valgrind ./bin/array_brovets <dat/brovets.txt
==2485== Memcheck, a memory error detector
==2485== Copyright (C) 2002-2013, and GNU GPL'd, by Julian Seward et al.
==2485== Using Valgrind-3.10.1 and LibVEX; rerun with -h for copyright info
==2485== Command: ./bin/array_brovets
==2485==
error: read of arr[39][0]

 name: BOROVETS
 rows: 58
 cols: 24

 1090 1091 1060 1137  542   52  768  721  620  737  108  660 1069  789  356 1361 1255 1014 1171  494   57  565  122 1252
 1052 1118   17  490  594   22 1375 1070  571  236   82   87 1170  266  873 1340  529 1164  129  700  255 1041  972  927
<snip>
==2485==
==2485== HEAP SUMMARY:
==2485==     in use at exit: 0 bytes in 0 blocks
==2485==   total heap usage: 39 allocs, 39 frees, 3,952 bytes allocated
==2485==
==2485== All heap blocks were freed -- no leaks are possible
==2485==
==2485== For counts of detected and suppressed errors, rerun with: -v
==2485== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 1 from 1)

有多种方法可以解决这个问题,所有方法都有效。很多事情都取决于你。如果您有任何问题,请查看并告诉我。

【讨论】:

  • Ubi maior... 考虑到你释放内存的方式,有件事让我很烦恼,不应该是 calloced brovets.row + 1 行吗?
  • 不,我认为混乱来了,在if (fscanf (fp, " %d", &amp;brovets.arr[i][j]) != 1) 中,如果遇到读取失败,row 将在那时被释放并且指针重置为NULL。那是结束free 之前唯一可以溜过的地方吗?回想一下,使用 calloc 方案,我们只需要遍历 有效指针 就可以确信我们已经释放了所有最后仍然分配的指针。
  • calloc 不设置指向 空指针 的指针,但会删除所有位。这不一定与空指针相同(NULL 是一个带有 空指针常量 的宏,在这里根本不适用)。类似于浮点0.0。只有整数才能保证 0 的所有位为零。它在这方面与默认初始化程序不同。
【解决方案2】:

OP的结构data中的成员arr是一个指向int的指针,所以我假设目的是将矩阵数据存储到一个动态分配的(行)数组中(列) ints.

这一行有一个问题:

m = (int **)malloc((ptr->row)*(ptr->col)*(sizeof(int)));

似乎分配了足够的空间来存储(行数)*(列数)ints,但是如果mint **(考虑演员表),我们应该首先分配( rows) 指向int 的指针,然后为所有矩阵元素提供足够的空间。

我将展示一个可能的实现,其中稍微修改了输入部分并添加了一些输入错误检查。我建议将代码(我懒洋洋地放在main() 中)拆分为更有用和可重用的函数。请注意,OP 发布的数据并不完全适合 58x24 矩阵。

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

typedef struct {
        char str[20];
        int row, col;
        int **arr;
} data;

int main() {
    FILE *f = fopen("input.txt","r");
    if ( !f ) {
        fprintf(stderr, "Error: Unable to open input file.\n");
        exit(EXIT_FAILURE);
    }
    int num;
    data mat;

    if ( fscanf(f,"%d %19s%d%d", &num, mat.str, &mat.row, &mat.col) != 4 ) {
        fprintf(stderr, "Error: wrong file format.\n");
        exit(EXIT_FAILURE);
    }
    printf("%d\n%s %d %d\n",num,mat.str,mat.row,mat.col);

    mat.arr = malloc(mat.row * sizeof *mat.arr);
    if ( !mat.arr ) {
        fprintf(stderr, "Error: malloc failed.\n");
        exit(EXIT_FAILURE);
    }

    // now, I prefer one single allocation to keep data contiguous
    mat.arr[0] = malloc(mat.row * mat.col * sizeof *mat.arr[0]);
    if ( !mat.arr[0] ) {
        fprintf(stderr, "Error: malloc failed.\n");
        free(mat.arr);
        exit(EXIT_FAILURE);
    }
    int i, j;
    for ( i = 1; i < mat.row; ++i ) {
        mat.arr[i] = mat.arr[i - 1] + mat.col;
    }

    for ( i = 0; i < mat.row; ++i ) {
        for ( j = 0; j < mat.col; ++j ) {
            fscanf(f,"%d",&mat.arr[i][j]);
        }
    }

    for ( i = 0; i < mat.row; ++i ) {
        for ( j = 0; j < mat.col; ++j ) {
            printf("%5d", mat.arr[i][j]);
        }
        printf("\n");
    }

    fclose(f);
    free(mat.arr[0]);
    free(mat.arr);
    return EXIT_SUCCESS;
}

【讨论】:

  • 动态分配空间也可以。方法如下: m = (int )malloc(rowcolsizeof(int)); for(i=0;icol+j)=atoi(line_buffer); } }
  • 当然,只需在data 结构中将arr 更改为int *,然后您也可以在该循环中使用*(m++)=atoi(line_buffer);
  • 这不是您的答案所暗示的二维数组。您可以使用 VLA 更轻松地分配具有可变大小的二维数组。那是标准的 C。
  • @Olaf 引用我自己的回答:“OP 的结构数据中的成员 arr 是指向 int 指针的指针”。我到底在哪里暗示二维数组?在语句的下一部分中,我是否使用了 “动态分配的(行)数组(列)整数数组。” ?如果您可以以更规范和标准化的方式重新表述相同的概念,请这样做,我不是专家,我的英语语言技能更差。我没有使用 VLA,因为 OP 一开始就没有(据我所知),所以我认为 VL2DA 不是最适合答案的。 VLA 在 C11 中是可选的,AFAIK。
  • “假设意图是将矩阵数据存储到(列)的(行)数组的动态分配数组中” - 您认为方法是唯一且正确的二维数组。这是完全错误的!
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2013-03-13
  • 2017-08-23
  • 2013-12-11
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多