根据我的任务,我需要读取一个使用C作为命令行参数传递的文件,并将其内容(每个字符)存储到2D数组中,以便以后可以更改数组的值并将更改后的内容保存到另一个文件中。 NVM的一些自定义功能。
这是我需要阅读的文件的示例:
#,#,#,#,#,#,.,#,.,.,.$
#,.,#,.,.,#,.,#,#,#,#$
#,.,#,.,.,.,.,.,.,#,#$
#,.,#,.,.,#,#,#,#,#,#$
#,.,.,#,.,.,.,.,.,.,#$
#,.,.,.,#,.,#,#,.,.,#$
#,.,.,.,.,#,.,.,.,.,#$
#,.,.,.,.,#,.,.,.,.,#$
#,.,.,.,.,.,.,.,.,.,#$
#,#,#,#,#,#,#,#,#,.,#$
这是我尝试过的:
int main(int argc, char *argv[]) {
int startX = 3;
int startY = 3;
int endX = 6;
int endY = 6;
int count = 0;
int x = 0;
int y = 0;
int fd = open(argv[1], O_RDONLY);
char ch;
if (fd == -1) {
mx_printerr("map does not exist\n");
exit(-1);
}
int targetFile =
open("path.txt", O_CREAT | O_EXCL | O_WRONLY, S_IWUSR | S_IRUSR);
while (read(fd, &ch, 1)) {
if (ch == '\n') {
x++;
}
if (ch != ',') {
count++;
}
}
fd = open(argv[1], O_RDONLY);
y = (count - x) / x;
char **arr;
arr = malloc(sizeof(char *) * x);
for (int i = 0; i < x; i++) arr[i] = malloc(y);
int tempX = 0, tempY = 0, tempCount = 0;
char tempString[count - x];
// the loop in question >>>>>
for (int i = 0; i < 10; i++) {
for (int j = 0; j < 11; j++) {
while (read(fd, &ch, 1)) {
if (ch != ',') {
arr[i][j] = ch;
// mx_printchar(arr[i][j]);
}
}
}
}
for (int i = 0; i < 10; i++) {
for (int j = 0; j < 11; j++) {
mx_printchar(arr[i][j]);
}
}
for (int i = 0; i < x; i++) free(arr[i]);
free(arr);
close(fd);
close(targetFile);
exit(0);
}
最后一个while循环应将文件内容保存到数组中。但是,当我尝试将数组的内容打印到控制台时,我得到了一些垃圾值: ���pp ����8��
[请帮助我了解这里有什么问题,或者应该使用另一种方法将数据保存到阵列中。
您起步不错,但是却迷失了处理读取和分配的笨拙方式。您可以通过多种方法将任意数量的字符和任意数量的行灵活地读取到动态分配的pointer-to-pointer-to char
对象中,该对象可以像二维数组一样进行索引。
一种无需预先读取每行字符来确定数量的简单方法是简单地为每行缓冲字符,然后分配存储空间并将该数量的字符复制到其最终位置。这提供了不必从某些预期字符数开始分配/重新分配每一行的优点。 (因为不能保证所有行的某处都不会有流浪字符)
另一种方法,同样有效,但是需要预先读取第一行,是读取第一行以确定字符数,为每一行分配该字符数,然后在随后的每一行中强制使用该字符数行(如果找到其他字符,则处理错误)。如果您想将每一行都视为一行,然后读取并创建一个字符串数组,则还有其他选择,但是您的要求似乎只是字符网格)。您可以在此时将行存储为字符串,只需添加一个终止符字符。
[下面,我们将使用固定缓冲区保存字符,直到找到'\n'
标记行的末尾(或者耗尽固定存储空间),然后为每一行动态分配存储空间并从固定位置复制字符缓冲到您的行存储。通常这是一个可行的解决方案,因为您将知道最大字符数的某些外边界,而不是每行可能出现的字符数(不要跳过)。即使您认为每行最多读取100个字符,2K缓冲区也很便宜。 (如果您在内存有限的嵌入式系统上,那么我会将缓冲区减少到预期最大字符数的2倍)如果您为固定缓冲区大小定义了一个常量上限,如果您发现需要更多,在文件顶部的一个位置进行简单的更改。
它如何工作?
让我们开始声明计数器变量以跟踪可用的指针数(avail
),行计数器(row
),列计数器(col
)以及固定数量的列(可用于与之进行比较)所有后续行中的列数(cols
)。声明您的固定缓冲区(buf
)和指针到指针以进行动态分配,并声明一个FILE*
指针来处理文件,例如
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define NCHARS 2048 /* if you need a constant, #define one (or more) */
int main (int argc, char **argv) {
size_t avail = 2, /* initial no. of available pointers to allocate */
row = 0, /* row counter */
col = 0, /* column counter */
cols = 0; /* fixed no. of columns based on 1st row */
char buf[NCHARS], /* temporary buffer to hold characters */
**arr = NULL; /* pointer-to-pointer-to char to hold grid */
/* use filename provided as 1st argument (stdin by default) */
FILE *fp = argc > 1 ? fopen (argv[1], "r") : stdin;
(<< [note:如果未提供任何参数,则程序将默认从stdin
读取)
avail
指针数量: if (!fp) { /* validate file open for reading */
perror ("file open failed");
return 1;
}
/* allocate/validate initial avail no. of pointers */
if (!(arr = malloc (avail * sizeof *arr))) {
perror ("malloc-arr");
return 1;
}
下一个而不是循环while ((c = fgetc(fp)) != EOF)
,而只是连续循环-这将使您可以在循环内处理'\n'
或EOF
,而不必在循环退出后单独处理最后一行的存储。首先从文件中读取下一个字符,并检查是否使用了所有avail
able指针(指示在继续操作之前,还需要再使用realloc()
):
while (1) { /* loop continually */ int c = fgetc(fp); /* read each char in file */ if (row == avail) { /* if all pointers used */ /* realloc 2X no. of pointers using temporary pointer */ void *tmp = realloc (arr, 2 * avail * sizeof *arr); if (!tmp) { /* validate reallocation */ perror ("realloc-arr"); return 1; /* return failure */ } arr = tmp; /* assign new block to arr */ avail *= 2; /* update available pointers */ }
(总是[note:
realloc()
使用临时指针。当realloc()
失败时(不是失败的话),它返回NULL
;如果您使用arr = realloc (arr, ..)
重新分配,则您刚刚覆盖了指向您的指针当前的内存块,其中NULL
导致指针丢失并且无法free()
先前分配的块,导致内存泄漏)现在检查是否已到达行尾或EOF
,如果是EOF
,如果col
计数为零,则知道在上一个EOF
之后到达了'\n'
,因此您可以只需在这一点上打破循环。否则,如果您以完整的列数达到EOF
,就知道您的文件缺少POSIX文件结尾,并且需要存储最后一行字符,例如 if (c == '\n' || c == EOF) { /* if end of line or EOF*/
if (c == EOF && !col) /* EOF after \n - break */
break;
if (!(arr[row] = malloc (col))) { /* allocate/validate col chars */
perror ("malloc-arr[row]");
return 1;
}
memcpy (arr[row++], buf, col); /* copy buf to arr[row], increment */
if (!cols) /* if cols not set */
cols = col; /* set cols to enforce cols per-row */
if (col != cols) { /* validate cols per-row */
fprintf (stderr, "error: invalid no. of cols - row %zu\n", row);
return 1;
}
if (c == EOF) /* break after non-POSIX eof */
break;
col = 0; /* reset col counter zero */
}
如果您的字符不是'\n'
或EOF
,只是普通字符,请将其添加到缓冲区中,检查缓冲区是否有下一个空间并继续操作:
else { /* reading in line */ buf[col++] = c; /* add char to buffer */ if (col == NCHARS) { /* if buffer full, handle error */ fputs ("error: line exceeds maximum.\n", stderr); return 1; } } }
至此,所有字符都存储在动态分配的对象中,您可以将其索引为2D数组。 (您还知道这只是存储不以nul结尾的字符,因此您不能将每一行都视为字符串)。如果愿意,可以随意添加一个以n结尾的字符,但是您最好将每行读入buf
和fgets()
并修剪尾随的换行符(如果存在)。取决于您的要求。该示例仅关闭文件(如果未从
stdin
中读取),则输出存储的字符并释放所有分配的内存,例如
if (fp != stdin) /* close file if not stdin */ fclose (fp); for (size_t i = 0; i < row; i++) { /* loop over rows */ for (size_t j = 0; j < cols; j++) /* loop over cols */ putchar (arr[i][j]); /* output char */ putchar ('\n'); /* tidy up with newline */ free (arr[i]); /* free row */ } free (arr); /* free pointers */ }
(这是整个程序,您可以将各个部分剪切/粘贴在一起)示例输入文件
$ cat dat/gridofchars.txt
#,#,#,#,#,#,.,#,.,.,.$
#,.,#,.,.,#,.,#,#,#,#$
#,.,#,.,.,.,.,.,.,#,#$
#,.,#,.,.,#,#,#,#,#,#$
#,.,.,#,.,.,.,.,.,.,#$
#,.,.,.,#,.,#,#,.,.,#$
#,.,.,.,.,#,.,.,.,.,#$
#,.,.,.,.,#,.,.,.,.,#$
#,.,.,.,.,.,.,.,.,.,#$
#,#,#,#,#,#,#,#,#,.,#$
示例使用/输出
$ ./bin/read_dyn_grid dat/gridofchars.txt
#,#,#,#,#,#,.,#,.,.,.$
#,.,#,.,.,#,.,#,#,#,#$
#,.,#,.,.,.,.,.,.,#,#$
#,.,#,.,.,#,#,#,#,#,#$
#,.,.,#,.,.,.,.,.,.,#$
#,.,.,.,#,.,#,#,.,.,#$
#,.,.,.,.,#,.,.,.,.,#$
#,.,.,.,.,#,.,.,.,.,#$
#,.,.,.,.,.,.,.,.,.,#$
#,#,#,#,#,#,#,#,#,.,#$
在您编写的任何可动态分配内存的代码中,对于任何已分配的内存块,您都有2个内存使用/错误检查
职责:(1)始终保留指向起始地址的指针,因此,( 2)不再需要它时,可以将其freed。
必须使用内存错误检查程序,以确保您不尝试访问内存或不要在分配的块的边界之外/之外写,尝试读取或基于未初始化的值进行条件跳转,最后,确认您释放了已分配的所有内存。对于Linux valgrind
是正常选择。每个平台都有类似的内存检查器。它们都很容易使用,只需通过它运行程序即可。
$ valgrind ./bin/read_dyn_grid dat/gridofchars.txt
==29391== Memcheck, a memory error detector
==29391== Copyright (C) 2002-2017, and GNU GPL'd, by Julian Seward et al.
==29391== Using Valgrind-3.13.0 and LibVEX; rerun with -h for copyright info
==29391== Command: ./bin/read_dyn_grid dat/gridofchars.txt
==29391==
#,#,#,#,#,#,.,#,.,.,.$
#,.,#,.,.,#,.,#,#,#,#$
#,.,#,.,.,.,.,.,.,#,#$
#,.,#,.,.,#,#,#,#,#,#$
#,.,.,#,.,.,.,.,.,.,#$
#,.,.,.,#,.,#,#,.,.,#$
#,.,.,.,.,#,.,.,.,.,#$
#,.,.,.,.,#,.,.,.,.,#$
#,.,.,.,.,.,.,.,.,.,#$
#,#,#,#,#,#,#,#,#,.,#$
==29391==
==29391== HEAP SUMMARY:
==29391== in use at exit: 0 bytes in 0 blocks
==29391== total heap usage: 17 allocs, 17 frees, 6,132 bytes allocated
==29391==
==29391== All heap blocks were freed -- no leaks are possible
==29391==
==29391== For counts of detected and suppressed errors, rerun with: -v
==29391== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 0 from 0)
始终确认已释放已分配的所有内存,并且没有内存错误。仔细检查,如果还有其他问题,请告诉我。