C / parse string, какой самый простой способ

#c #parsing #arrays

#c #синтаксический анализ #массивы

Вопрос:

У меня есть такая строка: 4;4=3;1=0,2=2,3=1,4=1,5=1;0003013340f59bce000002aaf01620e620198b2240002710;

Он разделен на разделы с помощью «;», И в каждом разделе может быть одна или несколько пар ключ / значение, например 5 = 1 и так далее, как вы можете видеть. Я хочу разобрать его на чистом C, и я начал работать с strtok тем, что я показываю в коде здесь:

 const wuint8 section_delimiter[] = ";";
const wuint8 field_delimiter[] = ",";
const wuint8 value_delimiter[] = "=";

printf("%sn",data->msg);

token = strtok(data->msg,section_delimiter);

while(token != NULL) {
    indicator = atoi(token);
    printf("indicator: %dn", indicator);
    switch(indicator) {
        case TYPE_1: {
            printf("type: %dn",TYPE_1);
            wuint16 i, headerType, headerSubType;

            for(i = 1; i < TP_MAX; i  ) {
                if(i == atoi(token)) {
                    token = strtok(NULL,value_delimiter);
                    headerType = i;
                    headerSubType = atoi(token);
                    break;
                }
            }
            break;
        }
        case TYPE_2: {
            printf("type: %dn",TYPE_3);
            break;
        }
        case TYPE_3: {
            printf("type: %dn",TYPE_3);
            break;
        }
        case TYPE_4: {
            printf("type: %dn",TYPE_4);
            break;
        }
 

Я не уверен, как это сделать правильно.
Это также усложняется, потому что не каждая строка имеет одинаковую структуру, иногда может присутствовать только один или два раздела. Например.: 3;4=3;1=0,2=2,3=1,4=1,5=1;

Есть how to ли способ, показывающий лучший и наиболее удобный способ?

Комментарии:

1. Пожалуйста, определите «лучший» в вашем случае или удалите это предложение.

2. верно. изменен на «самый простой» 🙂

3. Я бы сказал: разделяй и властвуй. Сначала разделите строку на три раздела, разделенные символом ‘;’. затем: разберитесь с отдельными разделами, возможно, в подфункции. Подфункция также может иметь дело с пустыми сегментами.

4. Я бы пошел со strtok() Я думаю, вы сделали правильный выбор. Это не выглядит (для меня, ymmv) так, как будто вы хотите использовать что-то вроде Lex / Yacc (но, если вы это сделаете, взгляните на Antlr antlr.org Мне очень нравится графический отладчик)

Ответ №1:

strtok AFAICR не может использоваться во вложенных циклах, подобных этому, из-за глобального состояния, которым он управляет сам. Я предлагаю сначала разобрать каждую часть, разделенную точкой с запятой, а затем обрабатывать их последовательно — или просто реализовать что-то похожее strtok на ваш регистр с запятой самостоятельно, а затем с удовольствием использовать strtok во внутреннем цикле.

Ответ №2:

С помощью strcspn(). Исправлены буферы, результаты переходят в глобальные переменные. буфер данных [] изменен (и, следовательно, должен быть доступен для записи). YMMV

 /*
It is separated into sections by ";" and each section can have one or more 
key/value pairs like 5=1 and so on, as you can see. I want to parse it in 
pure C and I started working with strtok as I am showing in code here:
*/
char data[] = "4;4=3;1=0,2=2,3=1,4=1,5=1;0003013340f59bce000002aaf01620e620198b2240002710;" ;

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

struct header {
    int l;
    int r;
    } headers[123];

unsigned nheader;
int indicator;
char rest [123];

int tokenise(char * buff);
unsigned tokenise2(struct header *dst, char * buff);

/****************/
int tokenise(char * buff)
{
char *ptrs[14];
unsigned nptr;
unsigned len, pos;

ptrs[nptr=0] = NULL;

for (len = pos=0;  buff[pos]; pos  = len ) {
    len = strcspn(buff pos, ";");
    ptrs[nptr  ] = buff pos;
    ptrs[nptr] = NULL;
        if (!buff[pos len] ) break;
        buff[pos len] = 0;
    len  =1;
    }

if ( nptr> 0 amp;amp; ptrs[0]) indicator = atoi(ptrs[0]); else indicator = -1;
if ( nptr> 1 amp;amp; ptrs[1]) nheader = tokenise2 (headers, ptrs[1] ); else nheader = 0;
if ( nptr> 2 amp;amp; ptrs[2]) nheader  = tokenise2 (headers nheader, ptrs[2] ); else nheader  = 0;
if ( nptr> 3 amp;amp; ptrs[3]) strcpy (rest, ptrs[3]); else rest[0] = 0;

return 0; /* or something useful ... */
}
unsigned tokenise2(struct header *target, char * buff)
{
char *ptrs[123];
unsigned nptr, iptr;
unsigned len, pos;

ptrs[nptr=0] = NULL;

for (len = pos=0;  buff[pos]; pos  = len ) {
    len = strcspn(buff pos, "," );
    ptrs[nptr  ] = buff pos;
    ptrs[nptr] = NULL;
    if (!buff[pos len] ) break;
    buff[pos len] = 0;
    len  =1;
    }

for ( iptr=0; iptr < nptr; iptr  ) {
    if (! ptrs[iptr] ) break;
    len = strcspn(ptrs[iptr], "=" );
    if (!len) break;
    target[iptr].l = atoi (ptrs[iptr] );
    target[iptr].r = atoi (ptrs[iptr] len 1 );
    }

return iptr; /* something useful ... */
}

int main(void)
{
int rc;
unsigned idx;

fprintf(stderr, "Org=[%s]n", data );
rc = tokenise(data);

printf("Indicator=%dn", indicator );
for (idx=0; idx < nheader; idx  ) {
    printf("%u: %d=%dn", idx, headers[idx].l , headers[idx].r );
    }

printf("Rest=%sn", rest );

return 0;
}