sscanf 和 scanset 停止读取十六进制数
sscanf and scanset stops reading of hex numbers
我尝试验证 UUID v4。我尝试使用 sscanf 执行此操作,如果可以使用 sscanf 完全读取 UUID(= 读取的字符总数 - 36),我认为这是一个正确的 UUID。到目前为止我的代码:
#include <stdio.h>
int main()
{
char uuid[ 37 ] = "da4dd6a0-5d4c-4dc6-a5e3-559a89aff639";
int a = 0, b = 0, c = 0, d = 0, e = 0, g = 0;
long long int f = 0;
printf( "uuid >%s<, variables read: %d \n", uuid, sscanf( uuid, "%8x-%4x-4%3x-%1x%3x-%12llx%n", &a, &b, &c, &d, &e, &f, &g ) );
printf( " a - %x, b - %x, c - %x, d - %x, e - %x, f - %llx, total number of characters read - %d \n", a, b, c, d, e, f, g );
return 0;
}
其中return以下输出
uuid >da4dd6a0-5d4c-4dc6-a5e3-559a89aff639<, variables read: 6
a - da4dd6a0, b - 5d4c, c - dc6, d - a, e - 5e3, f - 559a89aff639, total number of characters read - 36
到目前为止,一切正常。
现在我想包括,第三个连字符后的第一个字符需要是 [89ab] 之一。所以我把%1x%3x
改成了%1x[89ab]%3x
。但是现在,第一个字符被读取,其余的不再被读取。
输出:
uuid >da4dd6a0-5d4c-4dc6-a5e3-559a89aff639<, variables read: 4
a - da4dd6a0, b - 5d4c, c - dc6, d - a, e - 0, f - 0, total number of characters read - 0
我错过了什么?语法有什么问题?可以这样读吗?我尝试了几种扫描集和说明符的组合,但没有任何效果。
您可以不使用 sscanf()
来完成这项任务,您可以只编写一个简单的专用函数:
#include <ctype.h>
#include <string.h>
int check_UUID(const char *s) {
int i;
for (i = 0; s[i]; i++) {
if (i == 8 || i == 13 || i == 18 || i == 23) {
if (s[i] != '-')
return 0;
} else {
if (!isxdigit((unsigned char)s[i])) {
return 0;
}
}
if (i != 36)
return 0;
// you can add further tests for specific characters:
if (!strchr("89abAB", s[19]))
return 0;
return 1;
}
如果你坚持使用sscanf()
,这里是简洁的实现:
#include <stdio.h>
int check_UUID(const char *s) {
int n = 0;
sscanf(s, "%*8[0-9a-fA-F]-%*4[0-9a-fA-F]-%*4[0-9a-fA-F]-%*4[0-9a-fA-F]-%*12[0-9a-fA-F]%n", &n);
return n == 36 && s[n] == '[=11=]';
}
如果要优化第三个连字符后第一个字符的测试,请添加另一个字符 class:
#include <stdio.h>
int check_UUID(const char *s) {
int n = 0;
sscanf(s, "%*8[0-9a-fA-F]-%*4[0-9a-fA-F]-%*4[0-9a-fA-F]-%*1[89ab]%*3[0-9a-fA-F]-%*12[0-9a-fA-F]%n", &n);
return n == 36 && s[n] == '[=12=]';
}
备注:
%
后面的*
表示不存储转换,只跳过字符和1
表示最多消耗1
个字符。
- 为了让
sscanf
解析的字符数达到36个,所有十六进制数字序列必须恰好具有指定的宽度。
%n
导致 scanf
将到目前为止读取的字符数存储到下一个参数指向的 int
中。
- 您的转换规范对于获取实际的 UUID 编号很有用,但是
%x
格式接受前导白色 space、一个可选的符号和一个可选的 0x
或 0X
前缀,所有这些在 UUID 中都是无效的。您可以先验证 UUID,然后根据需要将其转换为各个部分。
Now I want to include, that the first character after the third hyphen needs to be one of [89ab]
. So I changed %1x%3x
to %1x[89ab]%3x
应该是"%1[89ab]%3x"
然后保存成2个字符的字符串。然后使用 strtol(..., ..., 16)
.
将该小字符串转换为十六进制值
相反,我建议对 universally unique identifier (UUID) 进行两步验证
:
检查语法,然后读取值。
我会避免使用 "%x"
,因为它允许前导空格、前导 '+','-'
和可选的前导 0x
以及窄输入。
为了验证,也许是一个简单的代码测试:
#include <ctype.h>
#include <stdio.h>
// byte lengths: 4-2-2-2-6
typedef struct {
unsigned long time_low;
unsigned time_mid;
unsigned time_hi_and_version;
unsigned clock_seq_hi_and_res_clock_seq_low;
unsigned long long node;
} uuid_T;
uuid_T* validate_uuid(uuid_T *dest, const char *uuid_source) {
static const char *uuid_pat = "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx";
const char *pat = uuid_pat;
const unsigned char *u = (const unsigned char*) uuid_source;
while (*u) {
if ((*pat++ == 'x' && !isxdigit(*u)) || *u != '-') {
return NULL;
}
u++;
}
if (*pat) { // Too short
return NULL;
}
sscanf(uuid_source, "%lx-%x-%x-%x-%llx", &dest->time_low,
&dest->time_mid, &dest->time_hi_and_version,
&dest->clock_seq_hi_and_res_clock_seq_low, &dest->node);
return dest;
}
u
是 unsigned char *u
所以 isxdigit(*u)
只用非负值调用所以避免 UB,
我尝试验证 UUID v4。我尝试使用 sscanf 执行此操作,如果可以使用 sscanf 完全读取 UUID(= 读取的字符总数 - 36),我认为这是一个正确的 UUID。到目前为止我的代码:
#include <stdio.h>
int main()
{
char uuid[ 37 ] = "da4dd6a0-5d4c-4dc6-a5e3-559a89aff639";
int a = 0, b = 0, c = 0, d = 0, e = 0, g = 0;
long long int f = 0;
printf( "uuid >%s<, variables read: %d \n", uuid, sscanf( uuid, "%8x-%4x-4%3x-%1x%3x-%12llx%n", &a, &b, &c, &d, &e, &f, &g ) );
printf( " a - %x, b - %x, c - %x, d - %x, e - %x, f - %llx, total number of characters read - %d \n", a, b, c, d, e, f, g );
return 0;
}
其中return以下输出
uuid >da4dd6a0-5d4c-4dc6-a5e3-559a89aff639<, variables read: 6
a - da4dd6a0, b - 5d4c, c - dc6, d - a, e - 5e3, f - 559a89aff639, total number of characters read - 36
到目前为止,一切正常。
现在我想包括,第三个连字符后的第一个字符需要是 [89ab] 之一。所以我把%1x%3x
改成了%1x[89ab]%3x
。但是现在,第一个字符被读取,其余的不再被读取。
输出:
uuid >da4dd6a0-5d4c-4dc6-a5e3-559a89aff639<, variables read: 4
a - da4dd6a0, b - 5d4c, c - dc6, d - a, e - 0, f - 0, total number of characters read - 0
我错过了什么?语法有什么问题?可以这样读吗?我尝试了几种扫描集和说明符的组合,但没有任何效果。
您可以不使用 sscanf()
来完成这项任务,您可以只编写一个简单的专用函数:
#include <ctype.h>
#include <string.h>
int check_UUID(const char *s) {
int i;
for (i = 0; s[i]; i++) {
if (i == 8 || i == 13 || i == 18 || i == 23) {
if (s[i] != '-')
return 0;
} else {
if (!isxdigit((unsigned char)s[i])) {
return 0;
}
}
if (i != 36)
return 0;
// you can add further tests for specific characters:
if (!strchr("89abAB", s[19]))
return 0;
return 1;
}
如果你坚持使用sscanf()
,这里是简洁的实现:
#include <stdio.h>
int check_UUID(const char *s) {
int n = 0;
sscanf(s, "%*8[0-9a-fA-F]-%*4[0-9a-fA-F]-%*4[0-9a-fA-F]-%*4[0-9a-fA-F]-%*12[0-9a-fA-F]%n", &n);
return n == 36 && s[n] == '[=11=]';
}
如果要优化第三个连字符后第一个字符的测试,请添加另一个字符 class:
#include <stdio.h>
int check_UUID(const char *s) {
int n = 0;
sscanf(s, "%*8[0-9a-fA-F]-%*4[0-9a-fA-F]-%*4[0-9a-fA-F]-%*1[89ab]%*3[0-9a-fA-F]-%*12[0-9a-fA-F]%n", &n);
return n == 36 && s[n] == '[=12=]';
}
备注:
%
后面的*
表示不存储转换,只跳过字符和1
表示最多消耗1
个字符。- 为了让
sscanf
解析的字符数达到36个,所有十六进制数字序列必须恰好具有指定的宽度。 %n
导致scanf
将到目前为止读取的字符数存储到下一个参数指向的int
中。- 您的转换规范对于获取实际的 UUID 编号很有用,但是
%x
格式接受前导白色 space、一个可选的符号和一个可选的0x
或0X
前缀,所有这些在 UUID 中都是无效的。您可以先验证 UUID,然后根据需要将其转换为各个部分。
Now I want to include, that the first character after the third hyphen needs to be one of
[89ab]
. So I changed%1x%3x
to%1x[89ab]%3x
应该是"%1[89ab]%3x"
然后保存成2个字符的字符串。然后使用 strtol(..., ..., 16)
.
相反,我建议对 universally unique identifier (UUID) 进行两步验证
:
检查语法,然后读取值。
我会避免使用 "%x"
,因为它允许前导空格、前导 '+','-'
和可选的前导 0x
以及窄输入。
为了验证,也许是一个简单的代码测试:
#include <ctype.h>
#include <stdio.h>
// byte lengths: 4-2-2-2-6
typedef struct {
unsigned long time_low;
unsigned time_mid;
unsigned time_hi_and_version;
unsigned clock_seq_hi_and_res_clock_seq_low;
unsigned long long node;
} uuid_T;
uuid_T* validate_uuid(uuid_T *dest, const char *uuid_source) {
static const char *uuid_pat = "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx";
const char *pat = uuid_pat;
const unsigned char *u = (const unsigned char*) uuid_source;
while (*u) {
if ((*pat++ == 'x' && !isxdigit(*u)) || *u != '-') {
return NULL;
}
u++;
}
if (*pat) { // Too short
return NULL;
}
sscanf(uuid_source, "%lx-%x-%x-%x-%llx", &dest->time_low,
&dest->time_mid, &dest->time_hi_and_version,
&dest->clock_seq_hi_and_res_clock_seq_low, &dest->node);
return dest;
}
u
是 unsigned char *u
所以 isxdigit(*u)
只用非负值调用所以避免 UB,