我有以下代码行从文本文件中读取输入:
a = scanf("(%d)%[^(](%d)(%d)", a1, arr, b1, c1);
此文件的正常输入行为: (4)1234(1)(1234)
除了检查scanf()
之外,%d
还可以用来标识与发送的模板格式不匹配的特定输入项吗? (在开头缺少括号等)。 4)1234(5)(1234)
根据我的研究,似乎scanf()
很难做到这一点,由于scanf()
返回4
(对于这种情况),这甚至可能是不可能的,但是我想看看是否有可以执行的解决方法。
答案 0 :(得分:0)
下面的我的解析器。我使用gnu扩展名fmemopen打开内存中的示例输入,并使用getline读取整行(和strerror_r)。
然后,我分析整行,返回错误负值,处理所有strtol可以设置的错误,处理超出范围的错误,错误的标记'('和')',太长的行,等等。
#define _GNU_SOURCE 1
#include <stdio.h>
#include <assert.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#include <limits.h>
#include <errno.h>
/**
* if expression 'expr' is true, then:
* print error message including custom message on a single line
* including errno and strerror
* then return the value "ret".
*/
#define RET_ON_ERR(expr, ret, msg, ...) do{ \
if(expr) { \
const int _errno_sav = errno; \
fprintf(stderr, \
"%s:%d: %s(): error: expresssion '%s' failed: ", \
__FILE__, __LINE__, __func__, #expr); \
if (_errno_sav) { \
char buf[22]; \
fprintf(stderr, "errno: %d '%s' ", \
_errno_sav, strerror_r(_errno_sav, buf, sizeof(buf))); \
errno = 0; \
} \
fprintf(stderr, "" msg "\n", ##__VA_ARGS__); \
return (ret); \
} \
}while(0)
/**
* Same as RET_ON_ERR but the value -__LINE__ is returned
*/
#define RET_LINE_ON_ERR(expr, msg, ...) RET_ON_ERR((expr), -__LINE__, msg, ##__VA_ARGS__)
/**
* Returns the array size of a statically allocated array
*/
#define ARRAY_SIZE(arr) (sizeof(arr)/sizeof(arr[0]))
/**
* Returns -1 and prints error message on conversion failure
* @param val0 outputted value
* @param ptr input string
* @param endptr0 outputted end pointer
* @param min minimum value to be returned
* @param max maximum value to be returned
* On error negative line number is returned and outputted variables are not set.
*/
int strtol_safe(long *val0, const char ptr[], const char *endptr0[], long min, long max)
{
assert(val0 != NULL);
assert(ptr != NULL);
RET_LINE_ON_ERR(!isdigit(ptr[0]), "string does not start with numbers '%s'", ptr);
// probably writing my own loop would be faster....
char *endptr = NULL;
errno = 0;
const long val = strtol(ptr, &endptr, 10);
RET_LINE_ON_ERR(errno == ERANGE && (val == LONG_MAX || val == LONG_MIN), "conversion out of range");
RET_LINE_ON_ERR(errno != 0, "internal error");
RET_LINE_ON_ERR(endptr == ptr, "no digits were found");
RET_LINE_ON_ERR(!(min <= val && val <= max), "%ld out of range (%ld,%ld)", val, min, max);
*val0 = val;
if (endptr0 != NULL) {
*endptr0 = endptr;
}
return 0;
}
/**
* Represents out data!
*/
struct data_s {
int a1;
char arr[20];
int b1;
int c1;
};
/**
* Fill single data variable parsing 'line'
* On error prints errorr message and returns negative value of the line where the error was found.
*/
int data_readline(struct data_s *data, const char line[])
{
assert(data != NULL);
assert(line != NULL);
int tmp;
long ltmp;
RET_LINE_ON_ERR(line++[0] != '(', "line '%s' first ( not found", line);
tmp = strtol_safe(<mp, line, &line, INT_MIN, INT_MAX);
RET_ON_ERR(tmp, tmp, "error on reading first number");
data->a1 = ltmp;
RET_LINE_ON_ERR(line++[0] != ')', "line '%s' first ) was not fond", line);
// read the second field consisting of only numbers until '(' is found
for (char *out = data->arr; line[0] != '('; ++line, ++out) {
RET_LINE_ON_ERR(!isdigit(line[0]),
"the second token must consist of numbers only");
RET_LINE_ON_ERR(out - data->arr >= ARRAY_SIZE(data->arr),
"array overflow when reading the second field from line '%s'", line);
*out = line[0];
}
RET_LINE_ON_ERR(line++[0] != '(', "line '%s' second ( not found", line);
tmp = strtol_safe(<mp, line, &line, INT_MIN, INT_MAX);
RET_ON_ERR(tmp, tmp, "error on reading first number");
data->b1 = ltmp;
RET_LINE_ON_ERR(line++[0] != ')', "line '%s' second ) not found", line);
RET_LINE_ON_ERR(line++[0] != '(', "line '%s' third ( not found", line);
tmp = strtol_safe(<mp, line, &line, INT_MIN, INT_MAX);
RET_ON_ERR(tmp, tmp, "error on reading first number");
data->c1 = ltmp;
RET_LINE_ON_ERR(line++[0] != ')', "line '%s' third ) not found", line);
RET_LINE_ON_ERR(line[0] != '\n' && line[0] != '\0',
"line '%s' does not end after reading tokens", line);
return 0;
}
int main()
{
// create FILE object reading from custom buffer, just for testing
char buf[] = "(4)1234(1)(1234)";
FILE * const f = fmemopen(buf, ARRAY_SIZE(buf), "r");
RET_LINE_ON_ERR(f == NULL, "error fmemopen");
// read one line of input.
char *line = NULL;
size_t n = 0;
RET_LINE_ON_ERR(getline(&line, &n, f) < 0, "getline error");
// read data structure
struct data_s data = {0};
int ret;
ret = data_readline(&data, line);
if (ret < 0) {
fprintf(stderr, "error from data_readline: %d\n", ret);
exit(-1);
}
// print output
printf("readed first line consist of:\n"
"a1=%d arr='%s' b1=%d c1=%d\n"
,
data.a1, data.arr, data.b1, data.c1
);
close(f);
return 0;
}
实时版本可在onlinegdb上使用。