Чтение файла с разделителями табуляции в структуру в C

У меня есть файл с данными, разделенными табуляцией. Я хочу прочитать каждую строку в структуре. У меня есть код для чтения данных в буфер char. Но я хочу загрузить данные в структуру.

Это мои образцы данных.

empname1\t001\t35\tcity1

empname2\t002\t35\tcity2

Определение моей структуры.

struct employee
{
  char *empname;
  char *empid;
  int age;
  char *addr;

};

Моя примерная программа для чтения данных в массив char buffer

char buffer[BUF_SIZE];      /* Character buffer */
    input_fd = open (fSource, O_RDONLY);
    if (input_fd == -1) {
       perror ("open");
        return 2;
    }
                    
    while((ret_in = read (input_fd, &buffer, BUF_SIZE)) > 0){
                    
         // Do Some Process 
    }

Здесь я хочу загрузить содержимое в структурную переменную вместо символьного буфера. Как я могу этого добиться?


person Smith Dwayne    schedule 16.12.2014    source источник


Ответы (4)


Ну, возможное решение может быть

  1. Прочитайте полную строку из файла, используя fgets().

  2. разметьте входной буфер на основе требуемого разделителя [tab в вашем случае], используя strtok().

  3. выделите память (malloc()/ realloc()) для переменной-указателя вашей структуры.

  4. скопируйте токенизированные входные данные в переменные-члены.

Примечание: 1. fgets() читает и сохраняет конечный \n. 2. Пожалуйста, внимательно проверьте, как использовать strtok(). Входная строка должна быть изменяемой. 3. Выделяйте память для указателей перед их использованием. IMO, используйте статически выделенный массив в качестве struct employee переменных-членов.

person Sourav Ghosh    schedule 16.12.2014
comment
Было бы легко удалить конечный newline с помощью strtok(buffer, "\t\n"). - person Weather Vane; 16.12.2014

Вы можете использовать функцию fscanf. Откройте файл как поток, затем используйте fscanf, чтобы получить входные данные из файла.

int fscanf(FILE *stream, const char *format, ...);
FILE *fp=fopen(fsource,"r+");
struct employee detail;
fscanf(fp,"%s %s %d %s",detail.empname,detail.empid,&detail.age,detail.addr);

Убедитесь, что выделение памяти для переменных.

Или же вы можете использовать функцию strtok. В этот раз вы должны использовать функцию sscanf.

person Karthikeyan.R.S    schedule 16.12.2014
comment
Использование любой из scanf() функций будет затруднено. Единственный способ заставить их уважать вкладки в качестве границ поля - это спецификация преобразования набора сканирования %[…], которая в настоящее время не используется в этом ответе. - person Jonathan Leffler; 16.12.2014
comment
Ваш fscanf прочитает целиком каждую строку в первую %s, т.е. detail.empname, он не будет токенизировать сам по себе, использование fscanf некорректно. Демо: ideone.com/3zKWI6. Примечание: detail.age должно быть &detail.age в fscanf. - person Saurabh Meshram; 17.12.2014
comment
В вашем вводе мы должны указать пространство табуляции, а не \t. Я взял \t из вопроса как спецификатор табуляции. - person Karthikeyan.R.S; 17.12.2014

Вы можете использовать fscanf для чтения каждой строки из файла, strtok для токенизации прочитанной строки.
Поскольку элементы вашей структуры являются указателями, распределяйте память соответствующим образом.

Следующий минимальный код делает именно то, что вы хотите.

#define SIZE 50 
FILE *fp = NULL;                                                            
int i = 0;                                                                  
struct employee var = {NULL, NULL, 0, NULL};                                
char line[SIZE] = {0}, *ptr = NULL;   

/* 1. Open file for Reading */                                                 
if (NULL == (fp = fopen("file.txt","r")))                                   
{                                                                           
    perror("Error while opening the file.\n");                              
    exit(EXIT_FAILURE);                                                     
}

/* 2. Allocate Memory */                                                       
var.empname = malloc(SIZE);                                                 
var.empid = malloc(SIZE);                                                   
var.addr = malloc(SIZE); 

/* 3. Read each line from the file */   
while (EOF != fscanf(fp, "%s", line))                                       
{                                                                           
    /* 4. Tokenise the read line, using "\" delimiter*/                     
    ptr = strtok(line, "\\");                                                                                   
    var.empname = ptr;                                                      

    while (NULL != (ptr = strtok(NULL, "\\")))                              
    {                                                                       
        i++;                                                                

        /* 5. Store the tokens as per structure members , where (i==0) is first member and so on.. */
        if(i == 1)                                                          
            var.empid = ptr;                                                
        else if(i == 2)                                                     
            var.age = atoi(ptr);                                            
        else if (i == 3)                                                    
            var.addr = ptr;                                                 
    }                                                                       

    i = 0;        /* Reset value of i */                                                          
    printf("After Reading: Name:[%s] Id:[%s] Age:[%d] Addr:[%s]\n", var.empname, var.empid, var.age, var.addr);
}                                                                           

Рабочая демонстрация: http://ideone.com/Kp9mzN

Несколько вещей, которые следует отметить здесь:

  1. Это гарантированно сработает, пока определение вашей структуры (и порядок членов) остается прежним (см. манипуляции со значением i).
  2. strtok(line, "\\");, Второй аргумент просто экранирует (первый \) фактический символ \.

Уточнение от ОП:

В определении вашей структуры третий член — это int, однако вы пытаетесь прочитать в него t35 (это строка).
Таким образом, var.age = atoi(ptr); даст вам 0,

Вы можете изменить определение структуры, сделав третий элемент char * и распределив память так же, как и другие элементы.

Или измените содержимое файла, убедившись, что int присутствует в качестве третьего значения.

person Saurabh Meshram    schedule 17.12.2014

Я думаю, это может быть то, что вы ищете

#include <string.h>
#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>
#include <unistd.h>
#include <fcntl.h>

#include <sys/stat.h>

struct employee
{
char *empname;
char *empid;
int age;
char *addr;

};

int readEmploee(char *line, struct employee *employee)
{
    char *token;
    char *saveptr;
    char *endptr;

    if ((employee == NULL) || (line == NULL))
        return 0;

    token = strtok_r(line, "\t", &saveptr);
    if (token == NULL)
        return 0;
    employee->empname = strdup(token);

    token = strtok_r(NULL, "\t", &saveptr);
    if (token == NULL)
        return 0;
    employee->empid = strdup(token);

    token = strtok_r(NULL, "\t", &saveptr);
    if (token == NULL)
        return 0;
    employee->age = strtol(token, &endptr, 10);
    if (*endptr != '\0')
        return 0;

    token = strtok_r(NULL, "\t", &saveptr);
    if (token == NULL)
        return 0;
    employee->addr = strdup(token);

    return 1;
}

char *mygetline(int fd)
{
    char  *line;
    size_t length;
    size_t count;
    char   character;

    line = malloc(128);
    if (line == NULL)
        return NULL;
    length = 0;
    count  = 1;
    do
    {
        if (read(fd, &character, 1) != 1) /* end of file probably reached */
        {
            free(line);
            return NULL;
        }
        else if (character != '\n')
        {
            if (length > 128 * count)
            {
                char *temp;
                temp = realloc(line, 128 * count);
                if (temp == NULL)
                {
                    free(line);
                    return NULL;
                }
                line   = temp;
                count += 1;
            }
            line[length++] = character;
        }
    } while (character != '\n');
    line[length] = 0;

    return line;
}

struct employee *readFile(const char *const fSource, size_t *count)
{
    struct employee *employees;
    int              employeeCount;
    int              input_fd;
    char            *line;

    if ((count == NULL) || (fSource == NULL))
        return NULL;

    *count        = 0;
    employees     = NULL;
    employeeCount = 0;
    input_fd      = open (fSource, O_RDONLY);
    if (input_fd == -1)
    {
        perror ("open");
        return NULL;
    }

    while ((line = mygetline(input_fd)) != NULL)
    {
        struct employee employee;
        if (readEmploee(line, &employee) != 0)
        {
            struct employee *temp;

            temp = realloc(employees, (1 + employeeCount) * sizeof(struct employee));
            if (temp != NULL)
                employees = temp;
            employees[employeeCount++] = employee;
        }
        free(line);
    }
    *count = employeeCount;

    return employees;
}

int
main()
{
    size_t           count;
    size_t           index;
    struct employee *employees;

    employees = readFile("somesamplefile.txt", &count);
    if (employees == NULL)
        return 1;
    for (index = 0 ; index < count ; index++)
    {
        struct employee current;

        current = employees[index];

        fprintf(stderr, "%s, %s, %d, %s\n", current.empname, current.empid, current.age, current.addr);
        if (current.empname != NULL)
            free(current.empname);
        if (current.empid != NULL)
            free(current.empid);
        if (current.addr != NULL)
            free(current.addr);
    }
    free(employees);
    return 0;
}
person Iharob Al Asimi    schedule 16.12.2014
comment
В общем случае следует исходить из того, что имена, начинающиеся со знака подчеркивания, зарезервированы для реализации. В деталях немного больше нюансов, но этого недостаточно, чтобы разумно было использовать такие имена в собственном коде. - person Jonathan Leffler; 16.12.2014
comment
Извините, дурная привычка. Починил это. Спасибо. - person Iharob Al Asimi; 16.12.2014