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

时间:2018-03-01 12:37:49

标签: c buffer strtok

我有一个包含数据类型和变量地址的字符串。这些值由" ///"分隔。它们是交替的(类型///地址///类型///地址...)。这些元组的数量不固定,并且可以在执行之间变化。

现在我的问题是如何在循环中处理字符串,因为需要首先使用原始字符串调用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是进一步程序的类型标识符。

2 个答案:

答案 0 :(得分: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

答案 1 :(得分:2)

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

文件: 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 != '\0')
            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 != '\0')
            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", $1, $3); 
       }
       ;

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

使用 gcc bison flex 进行编译非常简单

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

当然,这个程序需要一些调整,并根据您的需要调整它应该是直截了当的。

只需找到关于 bison flex 的简单教程,以帮助您完全理解此代码。