我正在尝试编写一个程序,该程序从文件中获取数字列表,并使用strtok()将每行数字存储在2D数组中。我遇到的问题是,当我尝试将值存储在2D数组的行中时,while循环进入无限循环,但是当我使用行token = strtok时(NULL,"& #34;)停止循环,我只是将每行的第一个值存储到每列的第一个单元格中。
我做错了什么?继承人是我的代码
int main() {
int myArray[240][30];
char line[240];
char *token;
int i, n, j;
FILE *fp;
fp = fopen("array_list.txt", "rt");
j = 0;
i = 0;
while (fgets(line, sizeof(line), fp)) {
token = strtok(line, " ");
while (token != NULL) {
n = atoi(token);
myArray[i][j] = n;
token = strtok(line, " ");
printf("%d ", myArray[i][j]);
token = strtok(NULL, " ");
j++;
}
printf("\n");
i++;
}
return 0;
}
以下是我试图阅读的文件示例。
1 98 48 55 46 12 48 43 7 73 47 33
97 26 67 25 67 47 46 74 67 43 78 98 90 58 6 5 53 19 37 42
81 84 36 70 59 65 99 56 94 23
79 73 27 47 98 78 8 2 3 43 84 19 59 98 51 72 2 13 55 3
20 61 25 84 28 70 56 23 52 69
86 70 60 90 72
51 71 92 91 55 6 72 67 73 64 78 43 65 30 49
99 82 40 72 5 44 20 21 22 72 99 74 27 59 25 1 26 26 22 66
63 27 54 46 46
91 65 77 45 94 94 95 11 77 46 64 14
69 70 75 48 68 22 55 78 1 4
31 76 55 12 93
答案 0 :(得分:3)
这里有很多问题。
首先j=0
必须在fgets
之后完成,否则永远不会重置。
其次,最大的错误是当你执行strtok(line," ")
时,你正在初始化strtok
状态机,你必须使用NULL
作为第一个参数循环,直到strtok
返回NULL
,但你没有这样做:你正在循环两个电话。作为改进,将多个选项作为分隔符传递:换行,制表和空格。
所以第二次字符串已经被标记化并且循环结束,但只有第一个项目。
此外,您必须为每一行存储j
的值,否则您将忘记每行上有多少元素(因为它是变量)。在下面的代码中,我使用第一个位置来存储大小,然后是项目。
我的修正提案(经过测试并适用于您的数据):
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main(){
const char *spaces = "\n\t ";
int myArray[240][30];
char line[240];
char *token;
int i, n, j;
FILE *fp;
fp=fopen("array_list.txt", "rt");
i = 0;
while(fgets(line, sizeof(line), fp))
{
j = 1;
token = strtok(line, spaces); // initialize line tokens
while(token) // won't enter if the line is empty, and stops when no more tokens
{
n = atoi(token); // strtol + error checking would be better
myArray[i][j] = n;
j++;
printf("%d ", myArray[i][j]);
token = strtok(NULL, spaces); // next token
}
myArray[i][0] = j; // use first array position to store the size
printf("\n");
i++;
}
return 0;
}
该计划仍然缺少一些内容:
i
&amp; j
&amp;如果超过限制就会中止。atoi
没有错误检查,因此输入“whatever”会导致0值。 strtol
允许错误检查。答案 1 :(得分:2)
虽然您可以使用strtok
拆分文件中的字词,然后使用atoi
执行转换为int
,但这种方法存在明显的弊端。首先,atoi
(以及所有atoX
函数)绝对无法验证您是否确实将atoi(word)
转换为数字。 (真的是0
吗? - 或转换是否失败?)
此外,strtok
+ atoi
是strtol
为您所做的一部分。 long strtol (const char *nptr, char **endptr, int base)
为您提供的其余内容通过errno
提供广泛的错误检查,并在下溢和LONG_MIN
上返回LONG_MAX
的范围检查< EM>溢出。它通过在转换期间未找到任何数字的情况下设置endptr = nptr
来提供进一步检查。更好的是,当转换成功时,endptr
被设置为转换的最后一个字符后面的下一个字符。 (它让你下次阅读!)
此外,您可以轻松地测试是否应该再次调用strtol
之前跳过下一个字符,从而允许您解析文件中的数字,而不管文件中包含哪些内容。换句话说,您可以编写一次整数读取例程,然后使用它进行最少的修改,以便读取您给出的任何文件。
虽然您没有指定如何从您为文件显示的144值中填充myArray[240][30];
,但以下示例显示了如何将这些值(以及几乎任何文件中的值)读取为阵列。您只需要添加一个额外的计数器或循环来在数组中按行添加x个值。
使用各种标题文件中提供的常量(例如errno.h
,limits.h
)来帮助您完成任务。以下示例从命令行上给出的文件名中读取(如果没有给出文件名,则默认来自stdin
),并将文件中找到的任何整数读取到数组arr
中,最大值为1024
值,例如
#include <stdio.h>
#include <stdlib.h> /* for strtol */
#include <string.h> /* for strlen */
#include <limits.h> /* for INT_MIN, INT_MAX */
#include <errno.h> /* for errno */
#ifndef BUF_SIZ /* set input buffer size */
#define BUF_SIZ 8192 /* gcc default, cl.exe (VS) uses 512 */
#endif
#define BASE 10 /* conversion base 10 */
#define AMAX 1024 /* max number of values to read */
int main (int argc, char **argv) {
int arr[AMAX] = {0}, /* array to hold values */
n = 0; /* number of values read */
char buf[BUF_SIZ] = ""; /* line buffer */
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, BUF_SIZ, fp)) { /* read each line */
char *p = buf, /* pointer to buf */
*ep = p; /* end pointer for strtol */
size_t len = strlen (buf); /* length of buf */
/* validate complete line read */
if (len == BUF_SIZ - 1 && buf[len - 1] != '\n') {
fprintf (stderr, "error: line too long.\n");
break;
}
while (n < AMAX && *p) { /* n < AMAX & each starting char */
errno = 0; /* reset errno */
long tmp = strtol (p, &ep, BASE); /* convert to long */
/* validate conversion */
if (p != ep) { /* digits converted */
/* if no error and in range - assign to array */
if (!errno && INT_MIN <= tmp && tmp <= INT_MAX)
arr[n++] = (int)tmp;
p = ep; /* set p to 1-past converted string */
}
/* find beginning of next number in buf */
for (; *p; p++) {
if ('0' <= *p && *p <= '9') /* positive value */
break; /* explicitly signed value */
if ((*p == '+' || *p == '-') && '0' <= *(p+1) && *(p+1) <= '9')
break;
}
}
}
if (fp != stdin) fclose (fp); /* close file if not stdin */
for (int i = 0; i < n; i++) /* output values read */
printf (" arr[%3d] = %d\n", i, arr[i]);
return 0;
}
示例输入文件
这样,您几乎可以读取包含整数值的任何文件。以下文件都包含相同的10个整数值。读取不需要POSIX EOF来读取文件中的最后一个值成功(例如,在最后一行之后的'\n'
)。下面的每个文件都被上面的代码读取并正确解析 - 无需修改。
$ cat dat/10int_comma.txt
8572,-2213,6434,16330,3034,12346,4855,16985,11250,1495
$ cat dat/10int.csv
8572, -2213, 6434, 16330, 3034
12346, 4855, 16985, 11250, 1495
$ cat dat/10int_5x2.txt
[[ 8572 -2213 ]
[ 6434 16330 ]
[ 3034 12346 ]
[ 4855 16985 ]
[ 11250 1495 ]]
$ cat dat/10intmess.txt
8572,;a -2213,;--a 6434,;
a- 16330,;a
- The Quick
Brown%3034 Fox
12346Jumps Over
A
4855,;*;Lazy 16985/,;a
Dog.
11250
1495
示例使用/输出
通过读取上述每个文件产生的输出是相同的。
$ ./bin/fgets_strtol <dat/10intmess.txt
arr[ 0] = 8572
arr[ 1] = -2213
arr[ 2] = 6434
arr[ 3] = 16330
arr[ 4] = 3034
arr[ 5] = 12346
arr[ 6] = 4855
arr[ 7] = 16985
arr[ 8] = 11250
arr[ 9] = 1495
虽然您可以自由使用strtok
和atoi
,但C库在strtol
中为该作业提供了一个工具(以及其余{{1}中的所有不同数字类型}} 功能)。仔细看看,如果您有其他问题,请告诉我。
答案 2 :(得分:0)
我认为OP已经给出了一些巧妙的技巧。
看起来像6行输入可能是其他东西。虽然我编辑了OP的帖子,但我在编辑器中远离"array_list.txt"
数据部分,出现,在最后一个数字后面有空行,空行,也许是一行超过30个数字。
换句话说,"array_list.txt"
的格式可能不如首次考虑,代码需要采取防御措施以避免过度运行等。
设置常量
魔法数字240,30,240几乎没有意义,因为2个数字是相同的值,代码中的角色可能会变得混乱 - 使用常量。
//int myArray[240][30];
//char line[240];
#define LINE_N 240
#define INT_PER_LINE_N 30
// I think this should be more like (INT_PER_LINE_N * 13)
#define LINE_SIZE 240
int myArray[LINE_N][INT_PER_LINE_N];
char line[LINE_SIZE];
首选strtol()
1>}
strtok()
很好寻找代币。代码提供了一个像strtok()
这样的分隔符,生活很美好。然而,如何处理非分隔符,非数字符号? " \n\t"
可以很好地处理整数。
strtol()