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是酸的