【发布时间】:2020-01-10 14:55:30
【问题描述】:
作为一段更大的代码的一部分,我编写了一组函数来扫描文件、跳过空格并将任何非负整数读取到数组中。问题是,当调用空格跳过代码时,如果我不使用 any 参数调用 printf(),则代码无法产生正确的输出。我已将问题隔离到eat_whitespace() 函数。
在我的代码中,我调用了 fseek(),并且我尝试了与该函数调用等效的各种变体,但是它们都没有帮助。我可以用 printf(NULL) 编译我的代码,但是,这似乎是一个巨大的问题。此外,我遇到了奇怪的行为,其中使用 kludge 的工作代码产生不正确的输出,但是当再次执行时,输出又恢复正确。这似乎表明我的代码没有正确关闭文件句柄,但确实如此,而且我已经验证它确实如此。真不知道是什么问题。
//#define KLUDGE1
//#define KLUDGE2
int is_ws_char (char c) {
char ws_chars[4] = {'\n', '\t', ' ', '\r'};
int detected_ws_char = 0;
for (int i = 0; i < 4; i++) {
if (c == ws_chars[i]) {
detected_ws_char = 1;
break;
}
}
return(detected_ws_char);
}
int eat_whitespace(FILE *data) {
char c;
while (fread(&c, sizeof(c), 1, data) == 1) {
//Problem lies with the following code...
//Seems that you need, for some strange reason, to issue a
//printf for the code to work...
#ifdef KLUDGE1
printf(NULL);
#endif
if (!is_ws_char(c)) {
#ifdef KLUDGE2
printf(NULL);
#endif
fseek(data, -1L, SEEK_CUR);
return(NOT_WHITESPACE);
}
}
return(END);
}
预期结果将是返回正确结果的数字解析代码。例如,this 输入应该产生以下输出,而不必使用 printf(NULL) "fix":
42
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
100
100
1001
2222
2002
3333
1
2
3
4
6
5
7
9
8
10
11
1
2
3
4
5
6
7
8
9
10
11
当不使用 printf(NULL) kludge 时,会生成以下错误输出:
42
12
22
32
42
52
62
72
82
92
10
11
12
13
14
15
16
17
18
19
20
100
1000
1001
2222
2002
3333
13
23
33
43
63
53
73
93
83
10
11
1
20
30
40
54
6
7
80
9
10
11
编辑:我已经将数字解析函数分解成他们自己的小测试程序,我将在下面粘贴
#include <stdio.h>
#include <stdlib.h>
#define END -1
#define NOT_WHITESPACE -2
#define NOT_NUMBER -2
#define NUM_SIZE_EXCEEDED -3
#define MAX_NUM_SIZE 257
//#define DEBUG1
//#define DEBUG2
//#define KLUDGE1
#define KLUDGE2
int is_ws_char (char c) {
char ws_chars[4] = {'\n', '\t', ' ', '\r'};
int detected_ws_char = 0;
for (int i = 0; i < 4; i++) {
if (c == ws_chars[i]) {
detected_ws_char = 1;
break;
}
}
return(detected_ws_char);
}
int eat_whitespace(FILE *data) {
char c;
while (fread(&c, sizeof(c), 1, data) == 1) {
//Problem lies with the following code...
//Seems that you need, for some strange reason, to issue a printf to stdout, with *any* argument eg. NULL for
//the code to work...
#ifdef KLUDGE1
printf(NULL);
#endif
if (!is_ws_char(c)) {
#ifdef KLUDGE2
printf(NULL);
#endif
fseek(data, -1L, SEEK_CUR);
return(NOT_WHITESPACE);
}
}
return(END);
}
int eat_number(FILE* data) {
char c;
char number_string[MAX_NUM_SIZE];
int num_size = 0;
int number;
int chars_read;
int token_type;
token_type = eat_whitespace(data);
int digit_detected = 0;
char digits[10] = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9'};
if (token_type == END) {
return(END);
}
while(1) {
if (num_size >= MAX_NUM_SIZE) {
return(NUM_SIZE_EXCEEDED);
}
chars_read = (int) fread((void *) &c, sizeof(char), 1, data);
if (!chars_read) {
return(END);
}
else {
for (int i = 0; i < 10; i++) {
if (digits[i] == c) {
digit_detected = 1;
number_string[num_size] = c;
num_size++;
break;
}
}
if(digit_detected) {
digit_detected = 0;
continue;
}
else {
if (is_ws_char(c)) {
fseek(data, -1L, SEEK_CUR);
number_string[(num_size+1)] = (char) 0x0;
number = atoi(number_string);
return(number);
}
else {
fprintf(stderr, "Invalid character : %c", c);
return(NOT_NUMBER);
}
}
}
}
}
int main(int argc, char **argv) {
int token_type = 0;
int n1 = 0;
char *input_file_name = (*(argv + 1));
FILE *input_file = fopen(input_file_name, "rb");
if (input_file == NULL) {
fprintf(stderr, "Can't open input file : %s\n", input_file_name);
return(1);
}
while (1) {
token_type = eat_whitespace(input_file);
if (token_type == END) {
fclose(input_file);
return(0);
}
else if (token_type == NOT_WHITESPACE ) {
n1 = eat_number(input_file);
switch(n1) {
case NOT_NUMBER : {
fclose(input_file);
fprintf(stderr, "Invalid number...\n");
return(1);
}
case NUM_SIZE_EXCEEDED : {
fprintf(stderr, "Exceeded maximum size of number string, which is 255 characters...\n");
fclose(input_file);
return(1);
}
default : {
if (n1 >= 0) {
printf("%i\n", n1);
break;
}
else {
fprintf(stderr, "Unknown error parsing number...\n");
fclose(input_file);
return(1);
}
}
}
}
else {
fclose(input_file);
fprintf(stderr, "Uknown error skipping whitespace...\n");
return(1);
}
}
fclose(input_file);
return(0);
}
它可能不漂亮,但你可以用例如编译它。 gcc -g -Wall -Wpedantic -O0 number_parser_test.c -o number_parser_test
编辑#2:我已将输入粘贴到下面的小测试程序中。我不知道标签是否会被保留,为什么我链接到 pastebin 上的相同文本
42 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
00100 100 1001
2222 2002
3333
1 2 3 4 6 5 7 9 8 10 11
00000001
02
0003
04
5
06
00007
8
09
010
000011
编辑#3:我可以用来演示问题的最小输入是上述文本的第一行,包含数字 42,然后是数字 1 到 20,包括空格和制表符。
【问题讨论】:
-
评论不用于扩展讨论;这个对话是moved to chat。
标签: c parsing null printf stdio