Как читать слово в слово, которые отделены только ":" из буфера? - PullRequest
0 голосов
/ 10 июля 2019

Я делаю переводчик языка и хочу читать из буфера слово за словом и сохранять их в структуре ключ-значение.

В буфере содержится такой файл:

hola:hello
que:what

и так далее. Я уже все перепробовал и сохраняю ошибки, такие как ошибка сегментации: 11 или просто читаю одну и ту же строку снова и снова.

struct key_value{
char *key;
char *value;
};

...

struct key_value *kv = malloc(sizeof(struct key_value) * count);
char k[20]; //key
char v[20]; //value
int x = 0;
for(i = 0; i < numbytes; i++){
    sscanf(buffer,"%21[^:]:%21[^\n]\n",k,v);
    (kv + i)->key = k;
    (kv + i)->value = v;
}

for(i = 0; i < count; i++){
    printf("key:  %s, value:  %s\n",(kv + i)->key,(kv + i)->value);
}

free(buffer);
free(kv);

Я ожидаю, что результат будет key: hola, value: hello key: que, value: what, но фактический результат просто key: hola, value: hello снова и снова.

Какой правильный способ сделать это?

Ответы [ 3 ]

1 голос
/ 10 июля 2019

Есть несколько проблем с вашим кодом, среди них

  • На каждой итерации цикла вы читаете из начала буфера. Естественно, что каждая итерация извлекает один и тот же ключ и значение.

  • В целом, ваша итерационная переменная цикла чтения, похоже, не имеет отношения к прочитанным данным. Похоже, что это итерация за байтом, но вы, похоже, хотите итерацию за строку . Возможно, вы захотите взглянуть на директиву scanf %n, которая поможет вам отслеживать прохождение через буфер.

  • Вы сканируете каждую пару ключ / значение в одни и те же локальные переменные k и v, затем назначаете указатели на эти переменные для своих структур. Результирующие указатели все одинаковые, и станут недействительными, когда функция вернется. Я предлагаю дать struct key_value` массивы для его членов вместо указателей и скопировать в них данные.

  • Ваш формат sscanf читает до 21 символа каждый для ключа и значения, но предоставленных массивов назначения недостаточно для этого. Вам нужно, чтобы они были рассчитаны как минимум на 22 символа, чтобы они содержали 21 плюс символ конца строки.

  • Ваш формат и использование sscanf() не поддерживают распознавание искаженного ввода, особенно ключей или значений с избыточной длиной. Вам нужно проверить возвращаемое значение, и вам, вероятно, нужно сопоставить завершающий символ новой строки с полем %c (буквальный символ новой строки в формате не означает, что вы думаете, что он означает).

Токенизация (весь буфер) с strtok_r или strtok или даже strchr вместо sscanf() может быть проще для вас.

Кроме того, примечание стиля: ваши выражения в форме (kv + i)->key допустимы, но было бы более идиоматично писать kv[i].key.

0 голосов
/ 10 июля 2019

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

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


struct key_value{
    char key[22];
    char value[22];
};

void parse_str(char* str, struct key_value** kv_arr, int* num){

    int  n    = 0;
    int  read = -1;
    char k[22];
    char v[22];
    int current_pos = 0;
    int consumed    = 0;

    /*counting number of key-value pairs*/
    while (1){
        if(current_pos > strlen(str)){
            break;
        }
        read = sscanf(str + current_pos, "%21[^:]:%21[^\n]\n%n", k, v, &consumed);
        current_pos += consumed;

        if(read == 2){
            ++n;
        }
    }
    printf("n = %d\n", n);
    *kv_arr = malloc(sizeof(struct key_value) * n);



    /*filling key_value array*/
    int i       = 0;
    read        = -1;
    current_pos = 0;
    consumed    = 0;
    while (1){
        if(current_pos > strlen(str)){
            break;
        }
        read = sscanf(str + current_pos, "%21[^:]:%21[^\n]\n%n", k, v, &consumed);
        current_pos += consumed;

        if(read == 2){
            struct key_value* kv = &((*kv_arr)[i]);
            strncpy(kv->key, k, 22);
            strncpy(kv->value, v, 22);
            ++i;
        }
    }

    *num = n;
}


int main(){

    char* str = "hola:hello\n"
                "que:what\n";

    int n;
    struct key_value* kv_arr;

    parse_str(str, &kv_arr, &n);

    for (int i = 0; i < n; ++i) {
        printf("%s  <--->  %s\n", kv_arr[i].key, kv_arr[i].value);
    }


    free(kv_arr);
    return 0;
}

вывод:

n = 2
hola  <--->  hello
que  <--->  what

Процесс завершен с кодом выхода 0


Примечание : sscanf работает на const char*, а не на входном потоке из файла, поэтому он НЕ хранит любую информацию о том, что он израсходовал.

решение :Я использовал %n в строке формата, чтобы получить количество символов, которые он уже использовал (стандарт C89).

0 голосов
/ 10 июля 2019

Я написал простой фрагмент кода, который может помочь вам решить вашу проблему. Я использовал функцию fgets для чтения из файла с именем file.txt и функцию strchr для индивидуализации первого вхождения разделителя ':'.

Вот код:

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

#define MAX_LINE_SIZE       256
#define MAX_DECODED_LINE    1024

struct decod {
    char key[MAX_LINE_SIZE];
    char value[MAX_DECODED_LINE];
};

static struct decod decod[1024];

int main(void)
{
    FILE * fptr = NULL;

    char fbuf[MAX_LINE_SIZE];
    char * value;
    int cnt=0,i;

    if ( !(fptr=fopen("file.txt","r")) )
    {
        perror("");
        return errno;
    }

    while( fgets(fbuf,MAX_LINE_SIZE,fptr)) {
        // Eliminate UNIX/DOS line terminator
        value=strrchr(fbuf,'\n');
        if (value) *value=0;
        value=strrchr(fbuf,'\r');
        if (value) *value=0;

        //Find first occurrence of the separator ':'
        value=strchr(fbuf,':');
        if (value) {
            // Truncates fbuf string to first word
            // and (++) points second word
            *value++=0;
        }

        if (cnt<MAX_DECODED_LINE) {
            strcpy(decod[cnt].key,fbuf);
            if (value!=NULL) {
                strcpy(decod[cnt].value,value);
            } else {
                decod[cnt].value[0]=0;
            }
            cnt++;
        } else {
            fprintf(stderr,
                 "Cannot read more than %d lines\n", MAX_DECODED_LINE);
            break;
        }
    }

    if (fptr)
        fclose(fptr);

    for(i=0;i<cnt;i++) {
        printf("key:%s\tvalue:%s\n",decod[i].key,decod[i].value);
    }

    return 0;
}

Этот код читает все строки (макс. 1024), которые содержит файл file.txt, загружает все отдельные пары (макс. 1024) в struct array decod, а затем распечатывает содержимое структуры.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...