strtok:如何将令牌存储在两个不同的缓冲区中

strtok: How to store tokens in two different buffers

我有一个包含数据类型和变量地址的字符串。这些值由“///”分隔,并且它们是交替的(类型 /// 地址 /// 类型 /// 地址 ...)。这些元组的数量不是固定的,并且会因执行而异。

现在我的问题是如何在循环中处理字符串,因为strtok需要先用原始字符串调用,然后用NULL参数调用,但在循环中它必须调用两次。因此,在第一个循环之后,strtok 被调用了三次,这导致 strtok 执行的计数不均匀,而它应该是偶数。我试图通过在循环外处理第一个元组(因为必须使用原始字符串调用 strtok)并在循环内处理其余元组来解决这个问题。

char mystring[128];
char seperator[] = "///";
char *part;
int type [128];
int address [128];
number_of_variables = 0;

part = strtok(mystring, seperator);
type[number_of_variables] = (int) atoi(part);
part = strtok(NULL, seperator);
address[number_of_variables] = (int)strtol(part, NULL, 16);

while(part != NULL){
    part = strtok(NULL, seperator);
    type[number_of_variables] = (int) atoi(part);


    part = strtok(NULL, seperator);
    address[number_of_variables] = (int)strtol(part, NULL, 16);

    number_of_variables++;
}

所以现在我的 strtok 执行次数是偶数,但是如果我的字符串包含例如 2 个元组,它将第二次进入循环,因此第五次调用 strtok,这会导致程序崩溃,因为 atoi( ) 得到一个错误的指针。

编辑: mystring 的示例:

"1///0x37660///2///0x38398"

1 和 2 是进一步程序的类型标识符。

我可以建议以下循环,如下面的演示程序所示。

#include <stdio.h>
#include <string.h>
#include <stdlib.h>

int main(void) 
{
    char mystring[128] = "1///0x37660///2///0x38398";
    char separator[] = "/ ";
    int type [128];
    int address [128];

    size_t number_of_variables = 0;

    for ( char *part = strtok( mystring, separator ); part; part = strtok( NULL, separator ) )
    {
        type[number_of_variables] = atoi(part);
        part = strtok( NULL, separator );
        address[number_of_variables] = part ? (int)strtol(part, NULL, 16) : 0;
        ++number_of_variables;
    }

    for ( size_t i = 0; i < number_of_variables; i++ )
    {
        printf( "%d\t%x\n", type[i], address[i] );
    }

    return 0;
}

程序输出为

1   37660
2   38398

您可以编写一个健壮且快速的解析器,保证可以正常工作并且没有这样的错误

文件:lexer.l

%{
#include <stdio.h>
#include "parser.tab.h"
int yyerror(const char *const message);
%}

%option noyywrap
%x IN_ADDRESS

DECIMAL [0-9]+
HEX "0x"[a-fA-F0-9]+
DELIMITER "///"

%%

<*>{DELIMITER} { return DELIMITER; }

<INITIAL>{DECIMAL} {
        char *endptr;
        // Make the lexer know that we are expecting a
        // hex number
        BEGIN(IN_ADDRESS);
        // Asign the value to use by bison
        yylval = strtol(yytext, &endptr, 10);
        // Check conversion's success
        if (*endptr != '[=10=]')
            return ERROR;
        return TYPE;
    }

<IN_ADDRESS>{HEX} {
        char *endptr;
        // Restore the initial state
        BEGIN(INITIAL);
        // Asign the value to use by bison
        yylval = strtol(yytext, &endptr, 16);
        // Check conversion's success
        if (*endptr != '[=10=]')
            return ERROR;
        return ADDRESS;
    }

%%

文件:parser.y

%{
#include <stdio.h>
extern int yylex();
extern FILE *yyin;

int yyerror(const char *const message);

#define YYSTYPE int
%}


%token TYPE
%token DELIMITER
%token ADDRESS
%token ERROR

%%

program:
       | program statement
       ;

command: TYPE DELIMITER ADDRESS { 
           fprintf(stdout, "type %d, address 0x%08x\n", , ); 
       }
       ;

statement: command
         | statement DELIMITER command;
         ;

%%

int
yyerror(const char *const message)
{
    return fprintf(stdout, "error: %s\n", message);
}

int
main(void)
{
    yyin = fopen("program.txt", "r");
    if (yyin == NULL)
        return -1;
    yyparse();
}

文件:program.txt

1///0x37660///2///0x38398

gccbisonflex 编译这个相当简单

bison -d parser.y
flex lexer.l
gcc -Wno-unused-function -Wall -Werror lex.yy.c parser.tab.c -o parserparser

当然,这个程序需要一些调整,根据您的需要进行调整应该很简单。

只需在 bisonflex 上找到一个简单的教程,以帮助您完全理解这段代码。