所以我目前正在开发一个使用txt文件数据的项目。提示用户输入文件名,txt文件的前两行是基本上包含txt文件的行和列值的整数。 在我的导师问的方式编写这个程序时,有两件事令我困惑。对于标准,她说:
最左边的列是标识符,而行的其余部分应该被视为行数据(浮点值)。 文件可能包含的示例是:
3
4
abc123 8.55 5 0 10
cdef123 83.50 10.5 10 55
hig123 7.30 6 0 1.9
我的代码:
//Creates array for 100 characters for filename
char fileName[100];
printf("Enter the file name to be read from: ");
scanf("%s", fileName);
FILE *myFile;
myFile = fopen(fileName, "r");
//Checks if file opened correctly
if (myFile == NULL)
{
printf("Error opening file\n"); //full file name must be entered
}
else {
printf("File opened successfully\n");
}
//gets value of records and value per records from file
//This will be the first 2 lines from line
fscanf(myFile, "%d %d", &records, &valuesPerRecords);
//printf("%d %d\n", records, valuesPerRecords); //Check int values from file
int counter = 0;
char *ptr_data;
ptr_data = (char*)malloc(records*(valuesPerRecords));
int totalElements = records*(valuesPerRecords);
/*If malloc can't allocate enough space, print error*/
if (ptr_data == NULL) {
printf("Error\n");
exit(-1);
}
int counter;
for (counter = 0; counter < totalElements; counter++){
fscanf(myFile, "%s", &ptr_data);
}
所以我想知道到目前为止,我是在正确的轨道上。我似乎无法想到让第一列作为字符串读入的方法,而其余的作为整数读入。我还必须稍后使用存储的值并对它们进行排序,但这对于以后的日期来说是个不同的问题。
答案 0 :(得分:2)
首先,你的教授显然希望你熟悉在字符串(标签)和数字(浮点值)的集合中遍历指针)使用指针算法而不使用数组索引。一个坚实的指针熟悉分配。
要处理标签,您可以使用指向指针的指针来键入char (双指针),因为每个指针都指向一个字符数组。您可以按如下方式声明和分配标签指针。 (这假设您已经阅读了输入文件中的rows
和cols
值)
char buf[MAXC] = "", /* temporary line buffer */
**labels = NULL, /* collection of labels */
**lp = NULL; /* pointers to walk labels */
...
/* allocate & validate cols char* pointers */
if (!(labels = calloc (rows, sizeof *labels))) {
fprintf (stderr, "error: virtual memory exhausted.\n");
return 1;
}
你可以为你的指针值做同样的事情,除了你只需要一个指针来输入double ,因为你只需要为一组双打分配。
double *mtrx = NULL, /* collection of numbers */
*p; /* pointers to walk numbers */
...
nptrs = rows * cols; /* set number of poiners required */
/* allocate & validate nptrs doubles */
if (!(mtrx = calloc (nptrs, sizeof *mtrx))) {
fprintf (stderr, "error: virtual memory exhausted.\n");
return 1;
}
使用指针lp
和p
至关重要,因为您无法增加labels
或mtrx
(不保存原始地址),因为这样做会失去指向分配给每个内存的内存开始的指针,立即导致内存泄漏(您无法释放该块)并阻止您再次访问该开头。每次您需要遍历labels
或mtrx
时,只需将起始地址指定给指针,例如
p = mtrx; /* set pointer p to mtrx */
lp = labels; /* set poiners lp to labels */
现在您可以以任何方式自由地读取和解析行,但我强烈建议使用面向行的输入函数将每行读入临时行缓冲区,然后使用sscanf
解析所需的值。仅使用fscanf
阅读有很多优点。在读取每一行之后,您可以在为字符串分配空间并分配值之前解析/验证每个值。
(注意:我在下面通过一次sscanf
调用作弊,你实际应该为char*
分配buf
指针,阅读label
,然后循环{{ 1}}次(可能使用cols
)检查每个值并分配给strtok/strtod
, - 留给你)
mtrx
然后,只需再次循环遍历值,根据需要使用或输出,然后 /* read each remaining line, allocate/fill pointers */
while (ndx < rows && fgets (buf, MAXC, fp)) {
if (*buf == '\n') continue; /* skip empty lines */
char label[MAXC] = ""; /* temp storage for labels */
double val[cols]; /* temp storage for numbers */
if (sscanf (buf, "%s %lf %lf %lf %lf", /* parse line */
label, &val[0], &val[1], &val[2], &val[3]) ==
(int)(cols + 1)) {
*lp++ = strdup (label); /* alloc/copy label */
for (i = 0; i < cols; i++) /* alloc/copy numbers */
*p++ = val[i];
ndx++; /* increment index */
}
}
if (fp != stdin) fclose (fp); /* close file if not stdin */
分配的内存。你可以用类似的东西来做到这一点:
free
这基本上是众多方法中的一种。把它们放在一起,你可以做到:
p = mtrx; /* reset pointer p to mtrx */
lp = labels; /* reset poiners lp to labels */
for (i = 0; i < rows; i++) {
printf (" %-10s", *lp);
free (*lp++);
for (j = 0; j < cols; j++)
printf (" %7.2lf", *p++);
putchar ('\n');
}
free (mtrx); /* free pointers */
free (labels);
使用的示例输入文件
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
enum { MAXC = 512 }; /* constants for max chars per-line */
int main (int argc, char **argv) {
char buf[MAXC] = "", /* temporary line buffer */
**labels = NULL, /* collection of labels */
**lp = NULL; /* pointers to walk labels */
double *mtrx = NULL, /* collection of numbers */
*p; /* pointers to walk numbers */
size_t i, j, ndx = 0, rows = 0, cols = 0, nptrs = 0;
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 (fgets (buf, MAXC, fp)) /* get rows, ignore blank lines */
if (sscanf (buf, "%zu", &rows) == 1)
break;
while (fgets (buf, MAXC, fp)) /* get cols, ignore blank lines */
if (sscanf (buf, "%zu", &cols) == 1)
break;
if (!rows || !cols) { /* validate rows & cols > 0 */
fprintf (stderr, "error: rows and cols values not found.\n");
return 1;
}
nptrs = rows * cols; /* set number of poiners required */
/* allocate & validate nptrs doubles */
if (!(mtrx = calloc (nptrs, sizeof *mtrx))) {
fprintf (stderr, "error: virtual memory exhausted.\n");
return 1;
}
/* allocate & validate rows char* pointers */
if (!(labels = calloc (rows, sizeof *labels))) {
fprintf (stderr, "error: virtual memory exhausted.\n");
return 1;
}
p = mtrx; /* set pointer p to mtrx */
lp = labels; /* set poiners lp to labels */
/* read each remaining line, allocate/fill pointers */
while (ndx < rows && fgets (buf, MAXC, fp)) {
if (*buf == '\n') continue; /* skip empty lines */
char label[MAXC] = ""; /* temp storage for labels */
double val[cols]; /* temp storage for numbers */
if (sscanf (buf, "%s %lf %lf %lf %lf", /* parse line */
label, &val[0], &val[1], &val[2], &val[3]) ==
(int)(cols + 1)) {
*lp++ = strdup (label); /* alloc/copy label */
for (i = 0; i < cols; i++) /* alloc/copy numbers */
*p++ = val[i];
ndx++; /* increment index */
}
}
if (fp != stdin) fclose (fp); /* close file if not stdin */
p = mtrx; /* reset pointer p to mtrx */
lp = labels; /* reset poiners lp to labels */
for (i = 0; i < rows; i++) {
printf (" %-10s", *lp);
free (*lp++);
for (j = 0; j < cols; j++)
printf (" %7.2lf", *p++);
putchar ('\n');
}
free (mtrx); /* free pointers */
free (labels);
return 0;
}
示例使用/输出
$ cat dat/arrinpt.txt
3
4
abc123 8.55 5 0 10
cdef123 83.50 10.5 10 55
hig123 7.30 6 0 1.9
内存使用/错误检查
在任何动态分配内存的代码中,对于分配的任何内存块,您都有2个职责:(1)始终保留指向起始地址的指针内存块,(2)当不再需要时,它可以释放。
您必须使用内存错误检查程序,以确保您没有在已分配的内存块之外/之外写入,尝试读取或基于未初始化的值跳转,最后确认您已拥有释放了你分配的所有内存。对于Linux $ ./bin/arrayptrs <dat/arrinpt.txt
abc123 8.55 5.00 0.00 10.00
cdef123 83.50 10.50 10.00 55.00
hig123 7.30 6.00 0.00 1.90
是正常的选择。
valgrind
始终确认释放所有堆块 - 不可能泄漏并且同样重要错误摘要:0个上下文中的0个错误。注意:某些操作系统不提供足够的泄漏和错误抑制文件(排除系统和操作系统内存不被报告为正在使用的文件),这将导致$ valgrind ./bin/arrayptrs <dat/arrinpt.txt
==22210== Memcheck, a memory error detector
==22210== Copyright (C) 2002-2013, and GNU GPL'd, by Julian Seward et al.
==22210== Using Valgrind-3.10.1 and LibVEX; rerun with -h for copyright info
==22210== Command: ./bin/arrayptrs
==22210==
abc123 8.55 5.00 0.00 10.00
cdef123 83.50 10.50 10.00 55.00
hig123 7.30 6.00 0.00 1.90
==22210==
==22210== HEAP SUMMARY:
==22210== in use at exit: 0 bytes in 0 blocks
==22210== total heap usage: 5 allocs, 5 frees, 142 bytes allocated
==22210==
==22210== All heap blocks were freed -- no leaks are possible
==22210==
==22210== For counts of detected and suppressed errors, rerun with: -v
==22210== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 1 from 1)
报告有些记忆尚未被释放(尽管你已经完成了自己的工作并释放了你分配并控制的所有区块。)
仔细看看,如果您有任何问题,请告诉我。