C++ 使用sscanf读取格式化字符串数据
我有以下代码:C++ 使用sscanf读取格式化字符串数据,c++,c,scanf,C++,C,Scanf,我有以下代码: int main(int argc, char* argv[]) { char tempBuf[100] = {"|BOD|01|02|100|ID000001|EOD|"}; char startSentinel[10], endSentinel[10], s1[10], s2[10], s3[10], s4[10]; sscanf((char *)tempBuf, "|%[^|]|%[^|]|%[^|]|%[^|]|%[^|]|%[^|]|", sta
int main(int argc, char* argv[])
{
char tempBuf[100] = {"|BOD|01|02|100|ID000001|EOD|"};
char startSentinel[10], endSentinel[10], s1[10], s2[10], s3[10], s4[10];
sscanf((char *)tempBuf, "|%[^|]|%[^|]|%[^|]|%[^|]|%[^|]|%[^|]|", startSentinel, s1, s2, s3, s4, endSentinel);
cout<<startSentinel<<" "<<s1<<" "<<s2<<" "<<s3<<" "<<s4<<" "<<endSentinel;
return 0;
}
输出:垃圾输出
如果我给出如下所示的空格:
char tempBuf[100] = {"|BOD| | |100|ID000001|EOD|"}; //Inserted space.
输出正确:
BOD 100 ID000001 EOD
谁能告诉我为什么?如何在不插入空格的情况下获得正确的输出?请注意,在调用
sscanf()
时不需要强制转换(char*)tempBuf
,因为数组名称在函数调用(以及大多数表达式)中会衰减为指针。问题在于扫描集[^ |]
匹配的一个或多个字符不是'|'
;如果未发生此类匹配,则匹配将失败并返回sscanf()
最简单的解决方案是使用BSD(包括macOS)和Linux函数将输入字符串解析为令牌。由于此函数修改输入字符串,因此您可能希望使用(POSIX)复制原始字符串strdup()
使用malloc()
为重复字符串分配内存,因此使用后需要空闲
d。此外,可能需要一个功能测试宏来启用这些功能
请注意,当发现两个分隔符相邻时,strep()
返回一个空字符串。在下面的代码中,假设第一个字符是分隔符,并跳过它。需要进行一些更改以处理格式不太严格的输入
#define _DEFAULT_SOURCE
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main(void)
{
char tempBuf[100] = {"|BOD|||100|ID000001|EOD|"};
char startSentinel[10], endSentinel[10], s1[10], s2[10], s3[10], s4[10];
char *delims = "|";
char *string = strdup(tempBuf);
char *next = string + 1; // skip first delimiter
char *token;
token = strsep(&next, delims);
strncpy(startSentinel, token, 10);
token = strsep(&next, delims);
strncpy(s1, token, 10);
token = strsep(&next, delims);
strncpy(s2, token, 10);
token = strsep(&next, delims);
strncpy(s3, token, 10);
token = strsep(&next, delims);
strncpy(s4, token, 10);
token = strsep(&next, delims);
strncpy(endSentinel, token, 10);
printf("%s %s %s %s %s %s\n", startSentinel, s1, s2, s3, s4, endSentinel);
free(string);
return 0;
}
上述两个程序都提供输出:
BOD 100 ID000001 EOD
请注意,在对sscanf()
的调用中不需要强制转换(char*)tempBuf
,因为数组名在函数调用(以及大多数表达式中)中衰减为指针。问题在于扫描集[^ |]
匹配的一个或多个字符不是'|'
;如果未发生此类匹配,则匹配将失败并返回sscanf()
最简单的解决方案是使用BSD(包括macOS)和Linux函数将输入字符串解析为令牌。由于此函数修改输入字符串,因此您可能希望使用(POSIX)复制原始字符串strdup()
使用malloc()
为重复字符串分配内存,因此使用后需要空闲
d。此外,可能需要一个功能测试宏来启用这些功能
请注意,当发现两个分隔符相邻时,strep()
返回一个空字符串。在下面的代码中,假设第一个字符是分隔符,并跳过它。需要进行一些更改以处理格式不太严格的输入
#define _DEFAULT_SOURCE
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main(void)
{
char tempBuf[100] = {"|BOD|||100|ID000001|EOD|"};
char startSentinel[10], endSentinel[10], s1[10], s2[10], s3[10], s4[10];
char *delims = "|";
char *string = strdup(tempBuf);
char *next = string + 1; // skip first delimiter
char *token;
token = strsep(&next, delims);
strncpy(startSentinel, token, 10);
token = strsep(&next, delims);
strncpy(s1, token, 10);
token = strsep(&next, delims);
strncpy(s2, token, 10);
token = strsep(&next, delims);
strncpy(s3, token, 10);
token = strsep(&next, delims);
strncpy(s4, token, 10);
token = strsep(&next, delims);
strncpy(endSentinel, token, 10);
printf("%s %s %s %s %s %s\n", startSentinel, s1, s2, s3, s4, endSentinel);
free(string);
return 0;
}
上述两个程序都提供输出:
BOD 100 ID000001 EOD
我想你想使用C++标签。因为<代码> %[^ ] ] /代码>拒绝<代码> <代码>。扫描集必须匹配至少一个字符,而不是0或更多。所以相邻的管道会导致它失效。如果测试了sscanf()
的返回值,就知道了。不能使用sscanf()
读取可能为零长度的字段。你必须重新考虑你的方法。@BLUEPIXY那么我可以用什么来避免拒绝|?你不能用strtok()
;它将相邻的分隔符视为单个分隔符。所以,您可能使用<代码> StcSPNE()/<代码> > StpBrk](<代码>),或者在代码> Sttok[](<代码> >上的一个变体,它不将相邻分隔符作为一个分隔符,如<代码> STRSET()/<代码>,如果它可用。我想您想使用C++标签。因为<代码> %^ ^ ^
拒绝|
。扫描集必须至少匹配一个字符,而不是零个或多个字符。所以相邻的管道会导致它失效。如果测试了sscanf()
的返回值,就知道了。不能使用sscanf()
读取可能为零长度的字段。你必须重新考虑你的方法。@BLUEPIXY那么我可以用什么来避免拒绝|?你不能用strtok()
;它将相邻的分隔符视为单个分隔符。因此,您可能使用strcspn()
或strpbrk()
,或strtok()
上的一个变体,该变体不将相邻的分隔符视为单个分隔符,如strep()
,如果可用的话;它不在里面。另外,\u DEFAULT\u SOURCE
在哪里定义?它不在POSIX或macOS(BSD)上。@JonathanLeffler——感谢您的评论和编辑。我误读了glibc手册,它将strtok_r()置于strep()之前;最后一句话说,strtok_r()
是POSIX,我设法在阅读strep()
条目时将其引入到我的阅读中!我在的Linux手册页上找到了\u DEFAULT\u SOURCE
;在.I用gcc-std=c99-Wall-Wextra-Wpedantic
编译的文件中也有一个关于它的条目,为了避免错误,必须包含feature test宏。\u xyz\u SOURCE
宏是一个永无止境的复杂性原因。有\u POSIX\u SOURCE
(已弃用)、\u POSIX\u C\u SOURCE
、\u XOPEN\u SOURCE
:这些都是POSIX和X/开放标准强制要求的。还有\u BSD\u SOURCE
,\u GNU\u SOURCE
,它们定义了BSD和GNU系统上的额外材料。Solaris上有\uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu
扩展,其他系统上可能还有其他扩展。我以前没见过\u DEFAULT\u SOURCE
;这只会增加混乱。按照手册页操作-但BSD上的strep()
不需要启用宏。生活不是很有趣。只是不同而已-生活中没有什么事情是那么简单的。请注意,strep()
是一个BSD(macOS)和Linux函数;它不在里面。另外,\u DEFAULT\u SOURCE
在哪里定义?它不在POSIX或macOS(BSD)上。@JonathanLeffler——感谢您的评论和编辑。我误读了glibc手册,它将strtok_r()置于strep()之前;最后一句话说,strtok_r()
是POSIX,我设法在阅读strep()
条目时将其引入到我的阅读中!我发现\u默认值\u是酸的