使用strtok的C标记正在打印出意外的值,并且阻碍了我的strtol验证

使用strtok的C标记正在打印出意外的值,并且阻碍了我的strtol验证,c,validation,buffer,strtok,strtol,C,Validation,Buffer,Strtok,Strtol,尝试使用strtok标记输入文件是 InputVector:0(0,3,4,2,40) 试图获取数字,但我遇到了一些我不理解的意外情况,我的标记代码如下所示 #define INV_DELIM1 ":" #define INV_DELIM2 "(" #define INV_DELIM3 ",)" checkBuff = fgets(buff, sizeof(buff), (FILE*)file); if(checkBuff == NULL)

尝试使用strtok标记输入文件是

InputVector:0(0,3,4,2,40)
试图获取数字,但我遇到了一些我不理解的意外情况,我的标记代码如下所示

    #define INV_DELIM1 ":"
    #define INV_DELIM2 "("
    #define INV_DELIM3 ",)"

    checkBuff = fgets(buff, sizeof(buff), (FILE*)file);

    if(checkBuff == NULL)
    {
        printf("fgets failure\n");
        return FALSE;
    }
    else if(buff[strlen(buff) - 1] != '\n')
    {
        printf("InputVector String too big or didn't end with a new line\n");
        return FALSE;
    }
    else 
    {
        buff[strlen(buff) - 1] = '\0';
    }

    token = strtok(buff, INV_DELIM1);
    printf("token %s", token);
    token = strtok(buff, INV_DELIM2);
    printf("token %s", token);

    while(token != NULL) {
            token = strtok(NULL, INV_DELIM3);
            printf("token %s\n", token);
            if(token != NULL) {
                number = strtol(token, &endptr, 10);
                if((token == endptr || *endptr != '\0')) {
                    printf("A token is Not a number\n");
                    return FALSE;
                }
                else {
                    vector[i] = number;
                    i++;
                }
            }
        }
输出:

token InputVector
token 0
token 0
token 3
token 4
token 2
token 40
token
因此,代码首先调用fgets并检查它是否大于缓冲区的长度,如果不是,则将最后一个字符替换为“\0”

然后我标记第一个单词和括号外的数字。while循环标记括号内的数字,并使用strtol对其进行更改,然后将其放入数组中。我试图使用strtol来检测括号内的数据类型是否为数字,但它总是检测错误,因为strtok读取的最后一个标记不在输入中。我怎样才能使最后一个标记不被读取,这样我的strtol就不会把它取出来?还是有更好的方法可以标记和检查括号内的值


输入文件稍后将包含多个输入向量,我必须能够检查它们是否有效。

当您首先使用函数strtok()时,您正在分隔定界器中的字符串:“(”之后的“e”。例如

 InputVector:0(0,3,4,2,40)
当您应用
strtok(buffer,“:”)
时,您只会得到第一个结果
InputVector
。您必须再次应用
strtok(NULL,“:”)
以获得剩余的分割
0(0,3,4,2,40)
。您不能对同一缓冲区应用不同的定界符,也不能在同一个buff中再次应用strtok,因为C拆分会在每个标记的末尾加上NULL,您将丢失引用,或者只对字符串的第一部分应用
strtok
。拆分此句子的最佳方法是使用所有定界符
:(),
,将像这样拆分所有句子:

InputVector
0
0
3
4
2
40
您需要做的更改是

#define INV_DELIM1 ":(),\n"
token = strtok(buff,INV_DELIM1); //for the first call of strtok
token = strtok(NULL,INV_DELIM1); //for the rest of strtok call

最可能的解释是,您的输入行以Windows换行符序列
\r\n
结尾。如果您的程序在unix(或linux)上运行,并且您在Windows上键入输入,Windows将发送两个字符的换行符序列,但unix程序不知道它需要进行换行符转换。(如果您在Windows系统上直接运行程序,只要您不以二进制模式打开文件,标准I/O库将通过将换行符序列转换为单个
\n
,为您处理换行符序列。)

由于
\r
不在分隔符列表中,
strtok
将其视为一个普通字符,所以最后一个字段将由
\r
组成。打印出来并不是一个完全无效的操作,但它是不可见的,所以很容易被愚弄到认为打印的是一个空字段。(如果字段仅由空格组成,也会发生同样的情况。)

您只需将
\r
添加到分隔符列表中即可。实际上,您可以将
\n
\r
添加到
strtok
调用中的分隔符列表中,这样您就不必担心修剪输入行。这将起作用,因为
strtok
将任何分隔符序列视为一个de限制器

但是,这可能不是您真正想要的,因为这将隐藏某些输入错误。例如,如果输入有两个连续的逗号,
strtok
将它们视为一个逗号,并且您永远不会知道该字段被跳过。您可以使用
strspn
而不是
strtok>来解决该特定问题
,但我个人认为更好的解决方案是根本不要使用
strtok
,因为
strtol
会告诉您行的终点

例如(为了简单起见,我省略了错误消息的打印。不必在该代码之前检查行是否以换行结束;如果您觉得有必要进行检查,可以在循环末尾找到右括号后进行检查。):

#包含/*用于“isspace”*/
#包括/*表示“假”*/
#包括/*用于“strtol”*/
#包括/*用于“strchr”*/
// ...
char*token=strchr(buff,:');/*查找冒号*/
if(token==NULL)返回false;/*无冒号*/
++令牌;/*符号位于令牌之后*/
char*endptr;
(void)strtol(token,&endptr,10);/*读取并扔掉一个数字*/
if(endptr==token)返回false;/*无数字*/
令牌=endptr;/*数字后面的字符*/
while(isspace(*token))++token;/*跳过空格(可能不需要)*/
if(*标记!='(')返回false;/*分隔符错误*/
对于(i=0;i