【问题标题】:strtok: How to store tokens in two different buffersstrtok:如何将令牌存储在两个不同的缓冲区中
【发布时间】:2018-03-01 12:37:49
【问题描述】:

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

现在我的问题是如何在循环中处理字符串,因为 strtok 需要首先使用原始字符串调用,然后使用 NULL 参数,但在循环中必须调用两次。因此,在第一个循环 strtok 被调用 3 次之后,这会导致 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 是后续程序的类型标识符。

【问题讨论】:

  • 你能提供一些输入字符串的例子吗?
  • 为什么在调用atoi 之前不检查part = strtok(NULL, seperator); 的结果是否为NULL?
  • 您知道strtok 将第二个参数视为要查找的单字符分隔符列表,而不是可能会查找的单分隔符一个字符串?因此,分隔符为"///",字符串为"1///0x37660/..." it's going to see a 1, then NULL, then NULL, then "0x37660". See the manual page for strtok`。
  • 添加到@lurker 的评论中,您可能想要使用strstr(),它与strtok() 不同的是,它是可重入的,适用于常量字符串(它不会修改它的参数 ) 并且您可以使用任何字符串作为分隔符。
  • 是的,弗拉德的解决方案将有效,因为他正确处理了strtok

标签: c buffer strtok


【解决方案1】:

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

#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

【讨论】:

  • 您的原始字符串包含 5 个值。所以它不是元组字符串,因为原始字符串必须包含偶数个值
  • @Gora 没关系。
  • 即使我不明白它也适用于您的解决方案。特别是?运算符和 :0 我不明白
  • @Gora 看来你的意思是条件或换句话说三元运算符。如果 strtok 返回 NULL,则默认情况下,数组地址元素的值设置为 0。您可以选择任何其他值或发出错误信号,而不是 0。
  • @Gora 在 C 中它们被称为三元运算符。
【解决方案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

gccbisonflex 编译这个相当简单

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

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

只要找一个关于bisonflex的简单教程来帮助你完全理解这段代码。

【讨论】:

  • 感谢您提供详细的解决方案。不幸的是,我的代码在一个更大项目的 Matlab S-Function 中运行,所以我无法更改编译器或项目指南。我不喜欢当前的解决方案,我不确定是否在某些情况下它不起作用(即使我还没有找到任何解决方案)但我的双手被束缚了;)
  • @Gora 我明白了,找到适合您情况的其他解析器生成器仍然不难。虽然我根本不懂matlab。
  • 对于手头的问题,这可能是矫枉过正。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2019-04-29
  • 2023-03-30
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2018-07-03
相关资源
最近更新 更多