SSCANF 用法非常具体的格式



我正在尝试将sscanf与完全采用以下格式的字符串一起使用:

整数

1 整数2

这样我就可以将 int1 和 int2 存储到一个数组中

我希望 sscanf 检查是否只输入了两个数字,只用一个空格分隔,根本没有前导或尾随空格。 应该和不应该工作的示例:

应该

4 7

不该

4 7
4 7[WHITESPACE] 
4    7
4 7a

我一直在尝试通过以下方式使用它

sscanf(str,"%d %d",&value1,&value2);
if (*STRING CONTAINS INVALID CHARACTERS OR WHITESPACE*){
exit from parent function }

第 1 步:读取该行(注意 OP 意味着代码以字符串开头,因此此步骤是说明性的。

char buf[100];
if (fgets(buf, sizeof buf, stdin) == NULL) Handle_EOF_or_Error();
// strip potential n
buf[strcspn(buf, "n")] = '';

检查是否只输入了两个数字,仅用一个空格分隔,根本没有前导或尾随空格

第 2 步:按格式扫描。"%d"允许前导空格 - 因此代码需要查看数字文本之前的内容。 使用"%n"存储扫描偏移量,以便以后进行空格检测。%*1[ ]扫描(不保存(1 个空间,正好 1 个空间。

int i[2];
int sep[3];
int cnt = sscanf(buf, "%n%d%*1[ ]%n%d%n", &sep[0], &i[0], &sep[1], &i[1], &sep[2]);
// 2 int            leading space?        leading space?         ?
if (cnt != 2 || isspace(buf[sep[0]]) || isspace(buf[sep[1]]) || buf[sep[2]]) {
Handle_Bad_Input();
} else {
Success();
}

莱夫勒@Jonathan的简化理念

int i[2];
int sep[2];
int cnt = sscanf(buf, "%d%*1[ ]%n%d%n", &i[0], &sep[0], &i[1], &sep[1]);
// 2 int        leading space?     leading space?           ?
if (cnt != 2 || isspace(buf[0]) || isspace(buf[sep[0]]) || buf[sep[1]]) { 

注意:迂腐的代码会用(unsigned char)来强制转换char以处理负charis...()因为unsigned char范围和EOF

定义了函数。
isspace(buf[...]) --> isspace((unsigned char) buf[...]) 

---

更可靠的方法是调用帮助程序函数来评估整数 - 可以根据代码的需求进行精心设计。 使用strtol()是定义性和溢出检测的良好起点。

#include <ctype.h>
#include <errno.h>
#include <limits.h>
#include <stdbool.h>
#include <stdlib.h>
// Return error status
bool scan_int(const char **input, int *dest) {
errno = 0;
const char *s = *input;
if (isspace((unsigned char) *s) {
// reject leading space
return true;
}
char *endptr;
long num = strtol(s, &endptr, 10);
if (s == endptr) {
// no conversion
return true;
}
*input = (const char *) endptr;
if (errno == ERANGE) {
// Overflow
return true;
}
#if LONG_MAX > INT_MAX  || LONG_MIN < INT_MIN
if (num > INT_MAX || num < INT_MIN) {
errno = ERANGE;
// Overflow
return true;
}
#endif
*dest = (int) num;
return false;
}
// Return error status - On success, return false
bool sz_int_space_int(const char *input, int *dest) {
return scan_int(&input, &dest[0]) || *input++ != ' '
|| scan_int(&input, &dest[1]) || *input;
}

如果您只处理没有前导符号的正整数,这里有一个更简单的解决方案:

#include <stdio.h>
#include <string.h>
int validate_string(const char *buf, int *int1, int *int2) {
size_t len1, len2;
/* return non 0 if and only if line has the correct format
and int1 and int2 contain the converted numbers */
return (len1 = strspn(buf, "0123456789")) > 0
&&     buf[len1] == ' '
&&     (len2 = strspn(buf + len1 + 1, "0123456789")) > 0
&&     buf[len1 + len2 + 1] == ''
&&     sscanf(buf, "%d%d", int1, int2) == 2);
}

但请注意,此解决方案未充分测试数值转换中的潜在溢出。

这是strtol()的替代方案

#include <ctype.h>
#include <stdlib.h>
int parse_int(int *dest, const char *buf, char **pp)
int validate_string(const char *buf, int *int1, int *int2) {
char *p1, *p2;
return !isspace((unsigned char)*buf)
&&     ((*int1 = strtol(buf, &p1, 10)), p1 > buf)
&&     *p1++ == ' '
&&     !isspace((unsigned char)*p1)
&&     ((*int2 = strtol(p1, &p2, 10)), p2 > p1)
&&     *p2 == '');
}

修改以避免 chux 推荐的实现定义行为:

#include <ctype.h>
#include <errno.h>
#include <limits.h>
#include <stdlib.h>
int parse_int(int *dest, const char *buf, char **pp) {
long val;
if (isspace((unsigned char)*buf)
return 0;
val = strtol(buf, pp, 10);
if (*pp <= buf)
return 0;
if (val < INT_MIN) {
val = INT_MIN;
errno = ERANGE;
} else
if (val > INT_MAX) {
val = INT_MAX;
errno = ERANGE;
}
*dest = (int)val;
return 1;
}
int validate_string(const char *buf, int *int1, int *int2) {
char *p1, *p2;
return parse_int(int1, buf, &p1)
&&     *p1++ == ' '
&&     parse_int(int2, p1, &p2)
&&     *p2 == '');
}

这是一个只有一个scanf()

#include <stdio.h>
int validate_string(const char *buf, int *int1, int *int2) {
int p1, p2, p3;
char c;
return sscanf(buf, " %n%d%1*[ ]%n %n%d%c", &p1, int1, &p2, &p3, int2, &c) == 2
&& p1 == 0 && p2 == p3;
}

最新更新