LINUX.ORG.RU

[ C ] парсинг строки


0

1

есть файл, в котором содержутся данные в виде:

params_1=«value1»;

params_2=«value2»;

.

.

.

params_N=«valueN»;

как правильно распарсить строку и вытащить value из кавычек в соответствуищий массив char для данной переменной(которые в оперативной памяти, а не в файле)

я делаю следующим образом:

1. открываю файл

2. вычитываю из файла строки, которые начинаются с «params_»

#define BUFF_SIZE 32

char str[BUFF_SIZE];

unsigned int i = 0;

while( fgets(str, BUFF_SIZE, pFILE) )

{

str[strlen(str) - 1] = '\0';

if( strstr( &str[0], «params_» ) == &str[0] )

{

printf(«%d: %s», i++, str);

}

}

в цикле нахожу строки, которые мне нужны.

мне нужно распарсить саму строку, т.е. «вытащить» имя переменной и ее значение без кавычек.

прошу помочь, спасибо!

man strtok (разделяем на токены по знаку =), кавычки отбрасываем при помощи strchr и strrchr. Ну, а если кавычки 100%-но находятся только в начале и конце параметра - дело еще больше упрощается.

Eddy_Em ☆☆☆☆☆ ()
char* get_qs_param(char *param){
        FNAME();
        char *tok, *val, *par, *str, *str0 = NULL, *ret = NULL;
        if(!qs && !get_qs()) return NULL;
        str = str0 = strdup(qs);
        tok = strtok(str, "& \n");
        do{
                if(strcasecmp(tok, param)==0){
                        ret = strdup(" "); // переменная есть, значения нет
                        break;
                }
                if((val = strchr(tok, '=')) == NULL) continue;
                *val++ = '\0';
                par = tok;
                if(strcasecmp(par, param)==0){
                        if(strlen(val) > 0)
                                ret = strdup(val);
                        else
                                ret = strdup(" "); // переменная есть, значения нет
                        break;
                }
        }while((tok = strtok(NULL, "& \n"))!=NULL);
        free(str0);
        return ret;
}

Остается лишь отбрасывание кавычек добавить

Eddy_Em ☆☆☆☆☆ ()

заюзать flex

зачем строить свои велопарсеры?

sdio ★★★★★ ()
Ответ на: комментарий от Eddy_Em

Eddy_Em, спасибо!

но как то сложно, может попроще как то можно, не разбивая строку, без strtok()

Cinewer ()
char val[256];
int i;

sscanf(line, "param_%d=\"%255[^\"]\";", &i, val);
arsi ★★★★★ ()
Ответ на: комментарий от Cinewer

> а что означает [^ ?

man scanf

[      Matches a nonempty sequence of characters from the specified set of accepted characters; the
       next pointer must be a pointer to char, and there must be enough room for all the characters
       in the string, plus a terminating null byte.  The usual skip of leading white space is  sup‐
       pressed.  The string is to be made up of characters in (or not in) a particular set; the set
       is defined by the characters between the open bracket [ character  and  a  close  bracket  ]
       character.   The set excludes those characters if the first character after the open bracket
       is a circumflex (^).  To include a close bracket in the set, make  it  the  first  character
       after  the  open bracket or the circumflex; any other position will end the set.  The hyphen
       character - is also special; when placed between two other characters, it adds all interven‐
       ing characters to the set.  To include a hyphen, make it the last character before the final
       close bracket.  For instance, [^]0-9-] means the set "everything except close bracket,  zero
       through  nine,  and  hyphen".  The string ends with the appearance of a character not in the
       (or, with a circumflex, in) set or when the field width runs out.
arsi ★★★★★ ()

В Глибе есть функционал по парсингу ini-файлов.

sched ()
Ответ на: комментарий от sdio

> \«%s\» — разве так нельзя?

тебя тоже в мане забанили?)

s      Matches a sequence of non-white-space characters […] The input string stops at white space or at
       the maximum field width, whichever occurs first.
arsi ★★★★★ ()
Ответ на: комментарий от akk

Это самая удобная функция. То, что строка изменяется, я знаю - для этого и используется strdup. Понятное дело, строки - не константы. Что такое «The identity of the delimiting character is lost» не понял. Ну и в потоках не использую.

Eddy_Em ☆☆☆☆☆ ()
Ответ на: комментарий от arsi

мне не для дела, иначе прочитал бы ман, а просто интересно было

sdio ★★★★★ ()
Ответ на: комментарий от arsi

Что-то не кажется мне безопасным sscanf использвоать.

Eddy_Em ☆☆☆☆☆ ()
Ответ на: комментарий от Deleted

franchukroman> Из пушки по воробью.

ну если ТС пишет одноразовую наколенную поделку, то да, перебор, а если с заделом на будущее?

sdio ★★★★★ ()
Ответ на: комментарий от sdio

Даже с заделом на будущее, усложнять без реальной нужды, вероятно, не следует.

slackwarrior ★★★★★ ()
Ответ на: комментарий от sdio

Если в будущем все усложнится и появится серьезная потребность в flex, никто не мешает переделать.

Deleted ()
Ответ на: комментарий от slackwarrior

не работает такая конструкция

sscanf(line, «param_%d=\»%255[^\«]\»;", &i, val);

val всегда пустой

Cinewer ()
Ответ на: комментарий от Cinewer

> val всегда пустой

покажи строку, которую парсишь.

arsi ★★★★★ ()
Ответ на: комментарий от Cinewer

> sscanf(line, «param_%d=\»%255[^\«]\»;", &i, val);

> params_1=«value1»;

а, да. невнимательно читал стартовый пост. замени «param_%d…» на «params_%d»:

sscanf(line, "params_%d=\"%255[^\"]\";", &i, val);

arsi ★★★★★ ()
Ответ на: комментарий от arsi

А разве он так не будет постоянно самую первую запись возвращать?

Eddy_Em ☆☆☆☆☆ ()
Ответ на: комментарий от arsi

не принципиально, params или param

sscanf не считывает то, что между кавычек

Cinewer ()
Ответ на: комментарий от arsi

param_1=«10000»;

param_2=«20»;

param_3=«5432»;

param_4=«hello»;

param_5=«psyhjsdfhksdf»;

param_6=«sdfsdfsdfsdfsdfsdfdsf»;

param_7=«123456789987»;

Cinewer ()
Ответ на: комментарий от Cinewer

УМВР.

$ cat test.c 
#include <stdio.h>

int main() {
    const char *line = "param_1=\"10000\";\n";

    char buf[256];
    int i;

    sscanf(line, "param_%d=\"%255[^\"]\";", &i, buf);

    printf("i=%d\n", i);
    printf("val=«%s»\n", buf);

    return 0;
}

$ cc test.c 
$ ./a.out
i=1
val=«10000»
$ _
arsi ★★★★★ ()
Ответ на: комментарий от Eddy_Em

> А разве он так не будет постоянно самую первую запись возвращать?

ты о чём? о_О

arsi ★★★★★ ()
Ответ на: комментарий от arsi

sscanf же не изменяет указатель и не возвращает указатель. А просто находит самое первое совпадение.

Т.е. это будет работать только для первого параметра в строке!

Eddy_Em ☆☆☆☆☆ ()
Ответ на: комментарий от arsi

arsi, спасибо!

а если первым будет не param_N, а слово переменной длины(т.е. название самой переменной будет переменной длины) и нужно вытащить имя переменной и ее значение?

не подскажите как sscanf будет выглядеть?

Cinewer ()
Ответ на: комментарий от arsi

Замени const char *line = «param_1=\„10000\“;\n»; на

const char *line = "param_1=\"10000\"; param_2=\"2000\"; param_100= \"4000\"  ;param_999 =\"  3.2" ";

Eddy_Em ☆☆☆☆☆ ()
Ответ на: комментарий от Cinewer

Я про конкретные конструкции ничо не говорил. (Или вы ответили чутка не тому ;))

slackwarrior ★★★★★ ()
Ответ на: комментарий от Eddy_Em

Что-то не кажется мне безопасным sscanf использвоать.

Перекрестиcь.

baverman ★★★ ()
Ответ на: комментарий от Cinewer
#include <stdio.h>

int main() {
    const char *line = "hello=\"world\";\n";

    char key[256];
    char val[256];

    sscanf(line, "%255[^=]=\"%255[^\"]\";", key, val);

    printf("key=«%s»\n", key);
    printf("val=«%s»\n", val);

    return 0;
}
arsi ★★★★★ ()
Ответ на: комментарий от Eddy_Em

> Замени const char *line = «param_1=\„10000\“;\n»; на …

ты наркоман? кончишь торчать, перечитай стартовый пост.

arsi ★★★★★ ()

разбери строку по регулярным выражениям (man regex)

#include <stdlib.h>
#include <stdio.h>
#include <sys/types.h>
#include <regex.h>
#include <string.h>
char *expr="([a-zA-Z_][a-zA-Z0-9]*)[[:space:]]*=[[:space:]]*\"([^\"]*)\"";
char buffer[1024];
int main() {
    regex_t reg;
    regmatch_t match[3];
    char *str;
    if (regcomp(&reg,expr,REG_EXTENDED|REG_ICASE)!=0) {
        exit(EXIT_FAILURE);
    }
    while(!feof(stdin)) {
        str=fgets(buffer,1024,stdin);
        if (str==NULL)
            continue;
        str+=strspn(str," \t\v");
        if (str[0]=='\0' || str[0]=='\n' || str[0]=='#')
            continue;
        if (regexec(&reg,str,3,match,0)!=0) {
            fprintf(stderr,"Error in string:%s",str);
            continue;
        }
        printf("variable=%.*s\n",match[1].rm_eo-match[1].rm_so,str+match[1].rm_so);
        printf("value=%.*s\n",match[2].rm_eo-match[2].rm_so,str+match[2].rm_so);
    }
    regfree(&reg);
    return 0;
}

MKuznetsov ★★★★★ ()
Ответ на: комментарий от arsi

Т.е. ты предлагаешь считывать построчно каким-нибудь getline, а потом sscanf'ом обрабатывать?

Eddy_Em ☆☆☆☆☆ ()
Ответ на: комментарий от Eddy_Em

> Т.е. ты предлагаешь считывать построчно каким-нибудь getline, а потом sscanf'ом обрабатывать?

если файл может содержать «неформатные» строки, типа комментов, то да, конечно:

#include <stdio.h>

int main() {
    char line[1024], key[256], val[256];

    while (fgets(line, sizeof line, stdin))
        if (sscanf(line, "%255[^=]=\"%255[^\"]\";", key, val) == 2)
            printf("key=«%s»\nval=«%s»\n", key, val);

    return 0;
}

иначе можно использовать только fscanf(3).

arsi ★★★★★ ()
Ответ на: комментарий от arsi

о господя размер буфера под строку у нормальных функций указывается с учетом терминатора а это какое то убожество

anonymous ()
Ответ на: комментарий от arsi

char val[256]; int i;

sscanf(line, «param_%d=\»%255[^\«]\»;", &i, val);

о господя размер буфера под строку у нормальных функций указывается с учетом терминатора а это какое то убожество

anonymous ()
Ответ на: комментарий от anonymous

strtok ужасно багоопасен но тот кто вороружен предупрежден ))) тема такая она (strtok) юзает механизм TLS (thread local storage) для сохранения между вызовами положения указателя для каждого потока имеется свой такой указатель но и это не спасает например вы пишите алгоритм в цикде вызываете strtok и между вызовами strtok вызываете какую нимбудь системную функцию так вот если она (а вы это знать не можете) тоже юзает strtok то она похеерит ваш указатель привязанный к текущему потоку когда вы вернетесь из системного вызова и вызовете strtok она начнет глючить потому что указатель похерен

anonymous ()
Ответ на: комментарий от anonymous

strtok можно использовать только если между вызовами (например вы в цикле что то парсите) нет вызовов незнакомых функций которые потенциально тоже могут юзать strtok

anonymous ()
Ответ на: комментарий от anonymous

наткнулся на это при попытке написать алгоритм для сохранения кукисов под вендой (в цикле вызывалаксь strtok а между вызовами был еще один цикл тоже юзающий strtok так вот при выходе из внутреннего цикла внешний оказывался неработоспособен) strtok гиблое дело и работающее со специфическими ограничениями в итоге я бросил писать алгоритм из за нее пошел другим путем

anonymous ()
Ответ на: комментарий от sdio

sdio> заюзать flex

Чтобы не быть голословным

$ cat input_test.txt

params_1="value1";
params_2="value2";
params_3="value3";
params_4="value4";
params_5="value5";
params_6="value6";
params_7="value7";
params_8="value8";
params_9="value9";
params_10="value10";


$ cat test1.l

%{
    int idx;
%}

%x left
%x right

%%

^params_    BEGIN(left);
<left>=\"   BEGIN(right);

<left>[0-9]+      {idx = atoi(yytext); } 

<right>([^\"]+)   {BEGIN(INITIAL);
                    /* add here malloc and strcpy */
                   printf("%d = %s\n", idx, yytext);}

\n                {BEGIN(INITIAL);}

.    { }

%%

main()
{
  yylex();
}


$ flex test1.l
$ gcc -O2 -o test1 lex.yy.c -lfl
$ ./test1 < input_test.txt

1 = value1
2 = value2
3 = value3
4 = value4
5 = value5
6 = value6
7 = value7
8 = value8
9 = value9
10 = value10

sdio ★★★★★ ()
Вы не можете добавлять комментарии в эту тему. Тема перемещена в архив.