Парсинг строки в си
Как максимально эффективно вытащить данные из строки? Имеется на входе:
" { 'Parametr1 2000' ; [ 5 & 2 ] ; [ 3 & 2 ] }{'ParametrLoooooooong 2021';[10&5];[ 6 &4]}"
"{'Parametr 2000' ; [ 5&2 ] ; [ 3 & 4 ] }"
"{ 'Parametr 2000';[ 5 & 2 ];[ 3 &10 ] }"
нужно:
- вытащить названия параметра в одинарных ковычках
- вытащить пары двух чисел в [ ] скобках, разделенные символом &
Хочу на выходе получить:
str 1 - Parametr1 2000 5 2 3 2
str 2 - ParametrLoooooooong 2021 10 5 6 4
str 3 - Parametr 2000 5 2 3 4
str 4 - Parametr 2000 5 2 3 10
Изначально планировал тремя ссканфами обойтись(на название параметра, на первцю пару чисел, на вторую), но не понимаю как из первого входа вытащить две строки(str 1 и str 2). Если кто-то поможет кодом, буду очень благодарен
Ответы (4 шт):
Что-то типа такого устроит?...
void getData(const char * s_)
{
char * s = _strdup(s_); // Чтоб можно было strtok пользовать
for(char * c = strtok(s,"}"); c; c = strtok(NULL,"}"))
{
char s1[128]; int a[4];
sscanf(c,"%*[^']'%[^']%*[^0-9]%d%*[^0-9]%d%*[^0-9]%d%*[^0-9]%d",s1,&a[0],&a[1],&a[2],&a[3]);
printf("%s %d %d %d %d\n",s1,a[0],a[1],a[2],a[3]);
}
free(s);
}
int main(int argc, const char * argv[])
{
const char * s[] = {
" { 'Parametr1 2000' ; [ 5 & 2 ] ; [ 3 & 2 ] }{'ParametrLoooooooong 2021';[10&5];[ 6 &4]}",
"{'Parametr 2000' ; [ 5&2 ] ; [ 3 & 4 ] }",
"{ 'Parametr 2000';[ 5 & 2 ];[ 3 &10 ] }"
};
for(int i = 0; i < sizeof(s)/sizeof(s[0]); ++i)
getData(s[i]);
}
Скорей всего последняя правка ответа.
Алгоритм ниже (по моим небольшим тестам) парсит строчку в массив по строгим правилам. Единственный минус алгоритма, который я на данный момент нашел - это то, что он парсит только целые числа, но если изменить функцию isNumber и использовать не atoi, то можно и под вещественные переписать.
В общем вот алгоритм:
Функция main:
char* str = (char*)" { 'Parametr1 2000' ; [ 5 & 2 ] ; [ 3 & 2 ] }{ 'ParametrLoooooooong 2021';[10&5];[ 6 &4]}";
Param params[10];
int count = 0;
char* s = str;
do{
s = strchr(s, '{');
if (s == NULL) break;
char* temp = getParam(s, params[count]);
if (temp != NULL) {
printf("str %d - %s %d %d %d %d\n", count + 1, params[count]);
++count;
}
else
printf("Error str\n");
++s;
} while (s != NULL);
Структура Param:
struct Param {
char* name;
int a1;
int b1;
int a2;
int b2;
};
Функция getParam:
char* getParam(char* str, Param& par) {
char* s = isNextSymb(str, '{');
if (s != NULL) {
s = getNameParam(str + 1, par.name);
if (s != NULL) {
s = isNextSymb(s + 1, ';');
if (s != NULL) {
s = getDataParam(s + 1, par.a1, par.b1);
if (s != NULL) {
s = isNextSymb(s + 1, ';');
if (s != NULL) {
s = getDataParam(s + 1, par.a2, par.b2);
if (s != NULL)
return (s = isNextSymb(s + 1, '}'));
}
}
}
}
}
return NULL;
}
Ну и дальше я просто все остальные функции выложу:
char* skipSpaces(char* str) {
while (*str != '\0' && *str == ' ') ++str;
return str;
}
char* isNextSymb(char* str, const char symb) {
str = skipSpaces(str);
if(*str == symb)
return str;
return NULL;
}
char* getNameParam(char* str, char*& out_str) {
char* s = isNextSymb(str, '\'');
if (s != NULL) {
++s;
char* temp = strchr(s, '\'');
if (temp == NULL) return NULL;
out_str = (char*)malloc(temp - s + 1);
char* n = out_str;
while (*s != '\0' && *s != '\'') {
*n = *s;
++n;
++s;
}
if (*s == '\0')
s = NULL;
*n = '\0';
}
return s;
}
char* isNumber(char* str) {
str = skipSpaces(str);
if (*str == '-') ++str;
str = skipSpaces(str);
while (*str != '\0' && *str >= '0' && *str <= '9') ++str;
if (*str == '\0') return NULL;
return str;
}
char* getDataParam(char* str, int& a, int& b) {
char* s = isNextSymb(str, '[');
if (s != NULL) {
a = atoi(s + 1);
s = isNumber(s + 1);
if (s != NULL) {
s = isNextSymb(s, '&');
if (s != NULL) {
b = atoi(s + 1);
s = isNumber(s + 1);
if (s != NULL)
return (s = isNextSymb(s, ']'));
}
}
}
return NULL;
}
Вот ссылка на код. Сразу хочу извинится, что код по ссылке на c++. Я не смог на этом сайте, запустить без ошибок на си.
Если хотите, чтобы я объяснил код, то отпишите в комментарии. Удачи :)
Еще один вариант, c проверкой синтаксиса, пробелы пропускаются:
#include <stdio.h>
#include <stdlib.h>
char *cur = NULL;
char text[256];
void skip_spaces() {
while (*cur == ' ') ++cur;
}
void ex(char c) { // expect char
skip_spaces();
if (*cur != c) {
printf("\nError: expected '%c' but not '%c'\n", c, *cur);
exit(-1);
}
++cur;
}
void str() { // string
ex('\'');
char* tp = text;
while(*cur != '\'' && *cur != '\0') {
*tp++ = *cur++;
}
*tp = '\0';
ex('\'');
printf("%s ", text);
}
void num() { // number
skip_spaces();
char* tp = text;
while(*cur >= '0' && *cur <= '9') {
*tp++ = *cur++;
}
*tp = '\0';
if (tp == text) {
printf("\nError: expected number but not '%c'\n", *cur);
exit(-1);
}
printf("%s ", text);
}
void two_p() { // two parameters
ex('['); num(); ex('&'); num(); ex(']');
}
int record = 0;
void parse_data(const char* s) {
cur = (char*) s;
while(*cur != '\0') {
printf("str %d - ", ++record);
ex('{'); str(); ex(';'); two_p(); ex(';'); two_p(); ex('}');
printf("\n");
}
}
int main() {
const char * s[] = {
" { 'Parametr1 2000' ; [ 5 & 2 ] ; [ 3 & 2 ] }{'ParametrLoooooooong 2021';[10&5];[ 6 &4]}",
"{'Parametr 2000' ; [ 5&2 ] ; [ 3 & 4 ] }",
"{ 'Parametr 2000';[ 5 & 2 ];[ 3 &10 ] }"
};
char ** sp = (char**) s;
char ** end = sp + sizeof(s) / sizeof(s[0]);
for(; sp != end; ++sp) {
parse_data(*sp);
}
}
UPDATE
Добавил генерацию дерева AST из описания грамматики (облегченная EBNF), неоптимально, но работает. Память не чистил.
#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>
#define ENTRIES 128
void handler_func(int index, char *s , int size);
char *cur = NULL;
typedef struct node {
enum node_type {TERM, NON_TERM, PARAM, OR, AND, REPEAT, DIGIT, LETTER} type;
char value; // for TERM
char* id; // for NON_TERM
int param_value; // for PARAM
struct node* left;
struct node* right;
} Node;
struct st_entry {
char* name;
Node* node;
};
struct st_entry st[ENTRIES] = {0};
int st_last_index = 0;
struct st_entry* find_entry(char *name) {
for (int j = 0; j < st_last_index; ++j) {
char* p = st[j].name;
char* ii = name;
// find entry by name
while (*p == *ii && *p != '\0') {
++p;
++ii;
}
if (*p == '\0' && *ii == *p) {
return &st[j];
}
}
return NULL;
}
struct st_entry* add_entry(char* name, Node* node) {
st[st_last_index].name = name;
st[st_last_index].node = node;
return &st[st_last_index++];
}
int st_num_param(char* name) {
char *p = "%";
char *n = name;
while (*p == *n && *p != '\0') {
++p;
++n;
}
if (*p != '\0') return -1;
return atoi(n);
}
void skip_spaces() {
while (*cur == ' ' || *cur == '\n') ++cur;
}
void ex(char c) { // expect char
skip_spaces();
if (*cur != c) {
printf("\nError: expected '%c' but not '%c'\n", c, *cur);
exit(-1);
}
++cur;
}
char* id() { // identificator
skip_spaces();
char* p = cur;
while (*cur != ' ' && *cur != '\0' && *cur != '}' && *cur != '|' && *cur != ';') {
++cur;
}
unsigned int size = cur - p;
char* ret = malloc(size + 1);
char *pret = ret;
for(unsigned int i = size; i != 0; --i) {
*pret++ = *p++;
}
pret = '\0';
return ret;
}
Node* expr() {
//enum node_type operation = AND;
skip_spaces();
Node* pnode = NULL;
while (*cur != '\0' && *cur != ';') {
//printf("operation = %d\n", operation);
if (*cur == '\'') {
// terminal
++cur;
char c = *cur;
if (c == '\0') {
printf("Error: expected a char\n");
exit(-1);
}
++cur;
//printf("terminal = '%c'\n", c);
Node* term = malloc(sizeof(Node));
term->value = c;
//if (c >= '0' && c <= '9') {
// term->type = DIGIT;
//} else {
term->type = TERM;
//}
ex('\'');
if (pnode) {
Node* anode = malloc(sizeof(Node));
anode->type = AND;
anode->left = pnode;
anode->right = term;
pnode = anode;
} else {
pnode = term;
}
skip_spaces();
} else if (*cur == '|') {
++cur;
Node* n = expr();
if (pnode) {
Node* anode = malloc(sizeof(Node));
anode->type = OR;
anode->left = pnode;
anode->right = n;
pnode = anode;
} else {
pnode = n;
}
skip_spaces();
} else if (*cur == '(') {
} else if (*cur == ')') {
} else if (*cur == '{') {
++cur;
Node* n = expr();
Node* rep = malloc(sizeof(Node));
rep->type = REPEAT;
rep->left = n;
rep->right = NULL;
if (pnode) {
Node* anode = malloc(sizeof(Node));
anode->type = AND;
anode->left = pnode;
anode->right = rep;
} else {
pnode = rep;
}
} else if (*cur == '}') {
++cur;
skip_spaces();
return pnode;
} else if (*cur == ' ') {
printf("SPACE HERE");
skip_spaces();
} else {
// non-terminal
char* name = id();
// printf("non-terminal = %s\n", name);
// search in symbol_table
struct st_entry * entry = find_entry(name);
if (entry) {
free(name);
} else {
// add in table
// printf("add in table: %s\n", name);
Node* anode = malloc(sizeof(Node));
anode->type = NON_TERM;
anode->id = name;
anode->left = NULL;
anode->right = NULL;
entry = add_entry(name, anode);
int num = st_num_param(name);
if (num != -1) {
entry->node->type = PARAM;
entry->node->param_value = num;
}
}
if (pnode) {
Node* anode = malloc(sizeof(Node));
anode->type = AND;//operation;
anode->left = pnode;
anode->right = entry->node;
pnode = anode;
} else {
pnode = entry->node;
}
skip_spaces();
}
}
return pnode;
}
Node* parse_grammar(const char * grammar) {
cur = grammar;
while(*cur != '\0') {
char* record = id();
// printf("%s\n", record);
struct st_entry* root_entry = find_entry(record);
if (!root_entry) {
Node* anode = malloc(sizeof(Node));
anode->type = NON_TERM;
anode->id = record;
anode->left = NULL;
anode->right = NULL;
root_entry = add_entry(record, anode);
}
ex('=');
Node* pnode = expr();
if(root_entry->node->type == NON_TERM) {
Node* p = root_entry->node;
p->type = pnode->type;
p->left = pnode->left;
p->right = pnode->right;
p->id = pnode->id;
p->param_value = pnode->param_value;
p->value = pnode->value;
free(pnode);
} else {
root_entry->node->left = pnode;
}
if (*cur == ';') ++cur;
skip_spaces();
}
struct st_entry* entry = find_entry("digit");
if (entry && entry->node->type == NON_TERM) {
// set "digit" to default if there is no NON_TERMINAL "digit"
// NON_TERM node shouldn't be presented in the end of creating of grammar AST
Node* p = entry->node;
p->type = DIGIT;
}
entry = find_entry("letter");
if (entry && entry->node->type == NON_TERM) {
// set "letter" to default if there is no NON_TERMINAL "letter"
// NON_TERM node shouldn't be presented in the end of creating of grammar AST
Node* p = entry->node;
p->type = LETTER;
}
return st[0].node;
}
bool visit(Node* node) {
if (node == NULL) {
printf("Node is NULL\n");
return false;
}
switch(node->type) {
case TERM:
skip_spaces();
if (node->value == *cur) {
printf("term=%c\n", *cur);
//printf("equal\n");
++cur;
return true;
}
//printf("Expected '%c' but not '%c'\n", node->value, *cur);
return false;
break;
/*
case NON_TERM:
printf("NON TERMINAL: %s\n", node->id);
return visit(node->left);
break;
//*/
case LETTER : {
//printf("visit LETTER: %c\n", *cur);
if ( (*cur >= 'a' && *cur <= 'z') || (*cur >= 'A' && *cur <= 'Z') ) {
printf("LETTER: %c\n", *cur);
++cur;
return true;
}
return false;
} break;
case DIGIT : {
//skip_spaces();
//printf("visit DIGIT: %c\n", *cur);
if (*cur >= '0' && *cur <= '9') {
printf("DIGIT: %c\n", *cur);
++cur;
return true;
}
return false;
}
break;
case REPEAT: {
//printf("REPEAT: %s\n", "g");
char* tmp = cur;
while(visit(node->left)) {
tmp = cur;
}
cur = tmp;
//printf("REPEAT end: %c\n", *cur);
return true;
}
break;
case PARAM:
skip_spaces();
//printf("PARAM: %s\n", node->id);
{
// remember string
char* begin_visit = cur;
bool r = visit(node->left);
int diff = (int)(cur - begin_visit);
handler_func(node->param_value, begin_visit, diff);
return r;
}
break;
case AND: {
skip_spaces();
//printf("AND\n");
bool a = visit(node->left);
if (a == false) return a;
//printf("AND: a = %d\n", a);
bool b = visit(node->right);
//printf("AND: b = %d\n", b);
return a && b;
}
break;
case OR: {
skip_spaces();
//printf("OR\n");
//printf("OR cur = %c\n", *cur);
char* tmp = cur;
bool a = visit(node->left);
//printf("OR a cur = %c\n", *cur);
//printf("OR: a = %d\n", a);
if (a) return true;
cur = tmp;
bool b = visit(node->right);
//printf("OR b cur = %c\n", *cur);
//printf("OR: b = %d\n", b);
return b;
}
break;
default:
// error
printf("Error: wrong node type\n");
exit(-1);
break;
}
}
/////////////////////////////////////////////////////////////////////////////////////////
struct record {
char* name;
int param[4];
};
struct record* precord = NULL;
struct record records[256];
int records_last_index = 0;
int current_param_index = 0;
void param0(char* s, int size) {
//printf("param0 executed\nstr=");
char* name = malloc(size + 1);
char* n = name;
while(size != 0) {
*n++ = *s;
//printf("%c", *s);
++s;
--size;
}
//printf("\n");
*n = '\0';
records[records_last_index].name = name;
}
void param1(char* s, int size) {
int r = atoi(s);
records[records_last_index].param[current_param_index++] = r;
}
void dummy_func(char* s, int size) {
printf("DUMMY FUNCTION executed\nstr=");
while(size != 0) {
printf("%c", *s++);
--size;
}
printf("\n");
}
void empty_func(char* s, int size) {
}
#define HANDLERS_SIZE 16
void (*handlers[HANDLERS_SIZE])(char *s , int size);
void init_handlers() {
handlers[0] = param0;
handlers[1] = param1;
for(int i = 2; i < HANDLERS_SIZE; ++i) {
handlers[i] = empty_func;
//handlers[i] = dummy_func;
}
}
void handler_func(int index, char *s , int size) {
handlers[index](s, size);
}
void parse_data(Node* gram, const char* s) {
cur = s;
while(*cur != '\0') {
bool result = visit(gram);
//printf("PARSE_DATA result = %d\n", result);
current_param_index = 0;
if (result) {
// make record
++records_last_index;
} else {
break;
}
}
}
int main() {
const char *grammar =
"record = '{' pstring ';' two_param ';' two_param '}' ;"
"pstring = ''' %0 ''' ;"
"%0 = {char} ;"
"char = letter | ' ' | digit ;"
"two_param = '[' %1 '&' %1 ']' ;"
"%1 = {digit} ;"
;
// "digit" is by default
// "letter" is by default
Node* gram = parse_grammar(grammar);
//*
printf("\nentries:\n");
for (int i = 0; i < st_last_index; ++i) {
printf("entry: %s\n", st[i].name);
}
printf("\n");
//*/
init_handlers();
const char * s[] = {
" { 'Parametr1 2001' ; [ 5 & 2 ] ; [ 3 & 2 ] }{'ParametrLoooooooong 2021';[10&5];[ 6 &4]}",
"{'Parametr 2002' ; [ 5&2 ] ; [ 3 & 4 ] }",
"{ 'Parametr 2003';[ 5 & 2 ];[ 3 &10 ] }"
};
char ** sp = (char**) s;
char ** end = sp + sizeof(s) / sizeof(s[0]);
for(; sp != end; ++sp) {
parse_data(gram, *sp);
}
for(int i = 0; i < records_last_index; ++i) {
printf("str %d = %s %d %d %d %d\n", i, records[i].name, records[i].param[0], records[i].param[1], records[i].param[2], records[i].param[3]);
}
}
Как обычно и бывает, более-менее подробный разбор с анализом ошибок и попытками продолжить работу после них выливается в достаточно длинную программу.
Хотя, основная функция разбора довольно компактна и легко перестаиваема под небольшие изменения формата данных.
// структура для разобранного фрагмента данных вида:
// { 'name of data' ; [1 & 2]; [3 & 4] } { ... } ...
struct dfr {
const char *fr_begin;
size_t fr_len;
const char *name;
size_t namelen;
int p1[2],
p2[2];
int str_error;
};
// returns data fragment length or 0 (if no more in str)
size_t
get_data_fragment (const char *str, struct dfr *p)
{
p->str_error = 0;
p->fr_len = 0;
// пропустим разделители до `{`
p->fr_begin = strspn(str, " \t\n\r") + str;
if (*(p->fr_begin) != '{') {
if (*(p->fr_begin)) // неверный символ разделитель
p->str_error = NO_FR_BEGIN;
return 0; // конец данных в строке ?
}
const char *scont = get_data_name(p->fr_begin + 1, p);
if (!scont)
return locate_fragment_end(str, &p->str_error);
// достаточно очевидный `sscanf()` для выборки числовой части данных
char c = 0;
int nchrs = 0,
rc = sscanf(scont, " ; [ %d & %d ] ; [ %d & %d %c%n",
p->p1, p->p1 + 1, p->p2, p->p2 + 1, &c, &nchrs);
if (rc != 5 || c != ']') {
fprintf(stderr, "invalid data pair(s) scanf = %d in: \"...", rc);
while (*scont && *scont != '}')
fputc(*scont++, stderr);
fputs("...\"\n", stderr);
p->str_error = INVALID_DATA;
}
size_t sz_ret = locate_fragment_end(scont + nchrs, &p->str_error);
if (sz_ret) {
p->fr_len = sz_ret + (scont + nchrs - p->fr_begin);
return p->fr_len + (p->fr_begin[p->fr_len] != 0);
}
return 0;
}
Эта функция вот так вызывается в цикле для обработки всех фрагментов данных в одной строке
for (char *str = data[i]; str && (l = get_data_fragment(str, &r)); str += l) {
if (r.str_error)
fprintf(stderr, "weak str_error %d\n", r.str_error);
print_fr_str(&r, ++n);
}
При выходе из цикла по получению в r.str_error кода NO_FR_BEGIN (т.е. при поиске начала фрагмента данных найден неверный символ-разделитель) можно попробовать найти начало фрагмента, вызвав str = strchr(str, '{'); (я не стал загромождать пример (и так, довольно "увесистый") подобными способами восстановления обработки после найденных ошибок).
Весь код
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
// структура для разобранного фрагмента данных вида:
// { 'name of data' ; [1 & 2]; [3 & 4] } { ... } ...
struct dfr {
const char *fr_begin;
size_t fr_len;
const char *name;
size_t namelen;
int p1[2],
p2[2];
int str_error;
};
#define NO_FR_BEGIN (1 << 0)
#define NO_FR_END (1 << 1)
#define NO_NAME_SQUOTE (1 << 2)
#define NO_NAME_EQUOTE (1 << 3)
#define INVALID_DATA (1 << 7)
static size_t
locate_fragment_end (const char *str, int *err)
{
const char *t = strchr(str, '}');
if (t)
return t - str + 1;
fputs("fragment end error\n", stderr);
*err |= NO_FR_END;
return 0;
}
static const char *
get_data_name (const char *s, struct dfr *p)
{
// пропустим разделители до `'`
size_t pos1 = strspn(s, " \t");
if (s[pos1] != '\'') {
fprintf(stderr, "no name begin %zd <%c>\n", pos1, s[pos1]);
p->str_error = NO_NAME_SQUOTE;
return 0;
}
// пропустим символы имени (ожидаем найти `'`)
size_t pos2 = strcspn(s + pos1 + 1, "{'[&]}");
if (s[pos1 + pos2 + 1] != '\'') {
fputs("no name end\n", stderr);
p->str_error = NO_NAME_EQUOTE;
return 0;
}
p->name = s + pos1 + 1;
p->namelen = pos2;
return s + pos1 + pos2 + 2;
}
// returns data fragment length or 0 (if no more in str)
size_t
get_data_fragment (const char *str, struct dfr *p)
{
p->str_error = 0;
p->fr_len = 0;
// пропустим разделители до `{`
p->fr_begin = strspn(str, " \t\n\r") + str;
if (*(p->fr_begin) != '{') {
if (*(p->fr_begin)) // неверный символ разделитель
p->str_error = NO_FR_BEGIN;
return 0; // конец данных в строке ?
}
const char *scont = get_data_name(p->fr_begin + 1, p);
if (!scont)
return locate_fragment_end(str, &p->str_error);
char c = 0;
int nchrs = 0,
rc = sscanf(scont, " ; [ %d & %d ] ; [ %d & %d %c%n",
p->p1, p->p1 + 1, p->p2, p->p2 + 1, &c, &nchrs);
if (rc != 5 || c != ']') {
fprintf(stderr, "invalid data pair(s) scanf = %d in: \"...", rc);
while (*scont && *scont != '}')
fputc(*scont++, stderr);
fputs("...\"\n", stderr);
p->str_error = INVALID_DATA;
}
size_t sz_ret = locate_fragment_end(scont + nchrs, &p->str_error);
if (sz_ret) {
p->fr_len = sz_ret + (scont + nchrs - p->fr_begin);
return p->fr_len + (p->fr_begin[p->fr_len] != 0);
}
return 0;
}
static void
print_fr_str (struct dfr *p, int n)
{
size_t l = p->fr_len;
const char *s = p->fr_begin;
l = p->namelen;
s = p->name;
#if DEBUG
printf("name: \"");
while (l--)
putchar(*s++);
printf("\" [%d %d] [%d %d]\n\n",
p->p1[0], p->p1[1], p->p2[0], p->p2[1]);
#else
printf("str%d -- ", n);
while (l--)
putchar(*s++);
printf(" %d %d %d %d\n",
p->p1[0], p->p1[1], p->p2[0], p->p2[1]);
#endif
}
int
main (int ac, char *av[])
{
char *data[] = {" { 'Parametr1 2000' ; [ 5 & 2 ] ; [ 3 & 2 ] }{ 'ParametrLoooooooong 2021';[10&5];[ 6 &4]}",
"{'Parametr 2000' ; [ 5&2 ] ; [ 3 & 4z ] }",
"{ 'Parametr 2000';[ 5 & 2 ];[ 3 &10 ] }"
};
int n = 0;
for (int i = 0; i < sizeof(data) / sizeof(data[0]); i++) {
#if DEBUG
puts(data[i]);
#endif
size_t l;
struct dfr r = {0};
for (char *str = data[i]; str && (l = get_data_fragment(str, &r)); str += l) {
if (r.str_error)
fprintf(stderr, "weak str_error %d\n", r.str_error);
print_fr_str(&r, ++n);
}
if (r.str_error)
printf("str_error %d <%s>\n", r.str_error, r.fr_begin);
}
return puts("End") == EOF;
}
Как обычно, компилируем, запускаем и смотрим результат
avp@avp-desktop:~/avp/hashcode$ gcc ttt.c -O3 -Wall
avp@avp-desktop:~/avp/hashcode$ ./a.out
str1 -- Parametr1 2000 5 2 3 2
str2 -- ParametrLoooooooong 2021 10 5 6 4
invalid data pair(s) scanf = 5 in: "... ; [ 5&2 ] ; [ 3 & 4z ] ..."
weak str_error 128
str3 -- Parametr 2000 5 2 3 4
str4 -- Parametr 2000 5 2 3 10
End
avp@avp-desktop:~/avp/hashcode$
UPDATE
Если снизить требования к допустимым символам-разделителям между фрагментами, открывающей фрагмент { и началом имени, символам составляющим имя и т.п., то код, конечно, можно немного сократить.
const char *
get_fr (const char *str, struct dfr *p)
{
p->str_error = 0;
p->fr_len = 0;
const char *s = str, *t;
if (!*s)
return 0;
if (p->fr_begin = strchr(str, '{')) {
if (p->name = strchr(s = p->fr_begin + 1, '\'')) {
p->name++;
if (t = strchr(s = p->name, '\'')) {
p->namelen = t - s;
char c = 0;
int nchrs = 0,
rc = sscanf(t + 1, " ; [ %d & %d ] ; [ %d & %d %c%n",
p->p1, p->p1 + 1, p->p2, p->p2 + 1, &c, &nchrs);
if (rc != 5 || c != ']') {
fprintf(stderr, "invalid data pair(s) scanf = %d in: \"...", rc);
s = t + 1;
while (*t && *t != '}')
fputc(*t++, stderr);
fputs("...\"\n", stderr);
p->str_error = INVALID_DATA;
return *t ? t + 1 : t;
}
if (s = strchr(t + nchrs, '}')) {// this is "good" return
p->fr_len = s - p->fr_begin + 1;
return s + 1;
}
p->str_error = NO_FR_END;
p->fr_len = t + nchrs - p->fr_begin + 1;
return t + nchrs;
} else
p->str_error = NO_NAME_EQUOTE;
} else
p->str_error = NO_NAME_SQUOTE;
} else {
p->str_error = NO_FR_BEGIN;
return 0;
}
if (s = strchr(str, '}')) {
p->fr_len = s - p->fr_begin + 1;
return s + 1;
}
return 0;
}
Для разнообразия я изменил возвращаемый тип на указатель. В этом случае использование функции может выглядеть вот так:
for (const char *str = data[i], *t; t = get_fr(str, &r); str = t) {
print_fr_str(&r, ++n);
}
P.S.
Если что-то непонятно или заинтересовало, спрашивайте.