C strtok разобрать сложную строку - PullRequest
0 голосов
/ 06 июня 2018

Здравствуйте, у меня сложная проблема в C. Я хочу разбить эту строку (в этом примере я включаю все возможности прерываний)

 "command1";"sleep 30; command2 -a ; command3";"command4="MyTest""

to:

command1
sleep 30; command2 -a ; command3
command4="MyTest"

строкаэто "табуляция", а элементы разделяются двойной кавычкой и ; Как я могу использовать strtok() для этого?

Я пытаюсь использовать strtok() с ";"разделитель как это:

   char mystring[1024]="\"command1\";\"sleep 30; command2 -a ; command3\";\"command4=\"MyTest\"\"";
   token = strtok(mystring, "\";\"");

   while( token != NULL ) {
      error(token);
      token = strtok(NULL, s);
   }

но у меня есть этот вывод:

command1
"sleep 30
command2 -a
command3"
"command4="MyTest""

Спасибо за аванс за вашу помощь

Ответы [ 2 ]

0 голосов
/ 06 июня 2018

Пример ниже анализирует входную строку по запросу.Потребуются некоторые корректировки, чтобы полностью соответствовать вашим потребностям (замените printf на фактический код обработки).

Чтобы понять код, начните с верхнего комментария, где описывается синтаксис вашей строки.затем перейдите к main - используйте отладчик.

Некоторые пояснения:

  • iss_t - симулякр входного потока строк.Он используется iss_get_char для чтения символов из входной строки.
  • wschar_t используется для хранения чтения символов из входного потока вместе с предыдущими пробелами.Это необходимо, поскольку вы не указали, могут ли быть пробелы между " и ;.Он используется iss_get_wschar и iss_peek_wschar.
  • iss_get_wschar читает пробелы до тех пор, пока не найдет символ, отличный от w.
  • iss_peek_wschar такой же, как указано выше, за исключением того, что он делаетnot извлечь символы из потока.
  • token_t описывает токены, из которых сделан поток.
  • parser_get_token извлекает токен из потока.Это, perharps, самая важная функция.
  • parser_commands извлекает все команды из потока и является основной функцией синтаксического анализатора.
  • parser_command извлекает команду из потока.

Вы можете проверить код здесь .

  #include <stdlib.h>
  #include <ctype.h>

  /*
  commands:
    EOF
    |
    command commands

  command:
    command_begin string command_end

  string:
    anything but command_end

  command_begin:
    QUOTE

  command_end:
    QUOTE SEMICOLON
    |
    QUOTE EOF
  */

  struct iss_t
  {
    char* s;
    char* g;
  };
  typedef struct iss_t iss_t;

  char iss_get_char( iss_t* self )
  {
    if ( !*self->g )
      return 0;
    return *self->g++;
  }

  struct wschar_t
  {
    char c;
    char s[ 1024 ];
  };
  typedef struct wschar_t wschar_t;

  char iss_get_wschar( iss_t* self, wschar_t* r )
  {
    char* p = r->s;

    while ( isspace( *p++ = r->c = iss_get_char( self ) ) )
      ;
    *p = 0;
    return r->c;
  }

  char iss_peek_wschar( iss_t* self )
  {
    char* savedg = self->g;
    wschar_t t;
    iss_get_wschar( self, &t );
    self->g = savedg;
    return t.c;
  }

  enum token_t
  {
    tk_done,
    tk_beg,
    tk_end,
    tk_rest
  };
  typedef enum token_t token_t;

  struct parser_t
  {
    iss_t* iss;
    wschar_t wschar;
    token_t token;
  };
  typedef struct parser_t parser_t;

  token_t parser_get_token( parser_t* self )
  {
    iss_get_wschar( self->iss, &self->wschar );

    // if done
    if ( self->wschar.c == 0 )
      return self->token = tk_done;

    // not a quote
    if ( self->wschar.c != '\"' )
      return self->token = tk_rest;

    // it is quote: check the next token
    switch ( iss_peek_wschar( self->iss ) )
    {
    case 0:
    case ';':
      iss_get_wschar( self->iss, &self->wschar ); // eat
      return self->token = tk_end;
    default:
      return self->token = tk_beg;
    }
  }

  int parser_command( parser_t* self )
  {
    printf( "COMMAND: " );
    if ( self->token != tk_beg )
    {
      printf( "command begin expected" );
      return -1;
    }

    while ( parser_get_token( self ) == tk_rest || self->token == tk_beg )
      printf( self->wschar.s );

    if ( self->token != tk_end )
    {
      printf( "command end expected" );
      return -1;
    }

    printf( "\n" );

    return 0;
  }

  void parser_commands( parser_t* self )
  {
    while ( 1 )
    {
      switch ( parser_get_token( self ) )
      {

      case tk_done:
        return;

      default:
        if ( parser_command( self ) < 0 )
          return;

      };
    };
  }


  int main()
  {
    char* s = "\"command1\"; \"sleep 30; command2 -a ; command3\"; \"command4=\"MyTest\"\"";

    iss_t iss = { s, s };
    parser_t parser = { &iss };
    parser_commands( &parser );

    return 0;
  }
0 голосов
/ 06 июня 2018

Вы должны иметь строку как

char str[]="\"command1\";\"sleep 30; command2 -a ; command3\";\"command4=\"MyTest\"\"";

Вам не нужно использовать strtok() можно использовать sscanf() как

char a[3][100];
sscanf(str, "\"%99[^\"]\";\"%99[^\"]\";\"%99s", a[0], a[1], a[2]);
a[2][strlen(a[2])-1]='\0';
printf("\n%s\n%s\n%s", a[0], a[1], a[2]);

Последний a[2] будет иметьдополнительные " в конце этого пути.Мы удаляем его с помощью a[2][strlen(a[2])-1]='\0';.

\"%[^\"]\" означает прочитать ", прочитать все до следующего " (эта часть хранится в массиве символов) и прочитать также ".

Спецификаторы ширины используются в sscanf() для предотвращения переполнения буфера.

Вы должны проверить, успешно ли sscanf(), проверив его возвращаемое значение, например

if( sscanf(........) != 3)
{
    printf("\nSomething went wrong.");
}

sscanf() возвращает количество успешных назначений, которое в этом случае должно быть 3.

Выходные данные:

command1
sleep 30; command2 -a ; command3
command4="MyTest"

Редактировать:

Размер массива 100был произвольным.Если требуется более точное распределение памяти, вы можете сначала использовать динамическое распределение памяти.Как и

sscanf(str, "\"%*[^\"]%n\";\"%*[^\"]%n\";\"%*s%n", &size[0], &size[1], &size[2]);
printf("\n%d, %d, %d", size[0], size[1], size[2]);
size[2] = size[2]-size[1]-4;
size[1] = size[1]-4;
size[0] = size[0]-1;
char *p[3];
for(int i=0; i<3; ++i)
{
    if( (p[i]=malloc(sizeof(char)*size[i]))==NULL )
    {
        perror("Memory allocation failed!");
        return 1;
    }
}

Спецификатор формата %n возвращает количество прочитанных символов.См. этот пост.

А * для подавления.т.е. значения просто отбрасываются.Это дополнительное scanf() делается для получения размеров строк.

Но это специфично для этого ввода.

...