Чтение из файла вызывает ошибку в последней строке

#c #linux #system-calls

Вопрос:

Проблема, с которой я сталкиваюсь, заключается в том, что, как только я хочу прочитать файл, программа распечатывает все, но не считывает последний элемент файла. Итак, в файле, допустим, у меня есть:

  • администратор:пропуск:1000:выключен
  • тест:testxx:1000:выкл.
  • ret:passx:1000:выкл.

Он выводит первые 2 строки, а затем говорит «Конец буфера» (это просто распечатка, которую я вставил в код, чтобы понять, в чем проблема). После распечатки «Конец буфера» выводится последняя строка.. Что я делаю не так?

 #include <stdlib.h> /* exit */
#include <errno.h> /* perror */
#include <stdio.h>
#include <unistd.h> /* write, read, close*/
#include <sys/types.h> 
#include <sys/stat.h> /*open */
#include <fcntl.h> 
#include <string.h>

#define MAX 500


struct utente{
    char username[MAX];
    char password[MAX];
    int gettoni;
    char stato[MAX];
    struct utente *next;
};

void menu(int);
struct utente* lettura_file(struct utente *);
struct utente* ins_coda(struct utente *t, char username[], char password[], char gettoni[], char stato[]);
void stampa_lista(struct utente*);
struct utente* elabora(struct utente *top, char buffer[]);
int check(struct utente *, char *);
void aggiornamento_file(struct utente *top);

int main(){
    while(1){
    char scelta[MAX], stringa[MAX];
    int i=0, res=0;
    struct utente *TOP=NULL;
    
    
    
    printf("1. STAMPA LISTAnInserisci scelta --> ");
    scanf("%s", scelta);
    
    i=atoi(scelta);
    switch(i){
        case 1:
            TOP=lettura_file(TOP);
            stampa_lista(TOP);
            /*printf("Inserisci stringa da trovare: ");
            scanf("%s", stringa);
            res=check(TOP, stringa);
            if(res==1)
                aggiornamento_file(TOP);
            */
            break;
        }
    
    }
    return 0;
}



struct utente* lettura_file(struct utente *top){
    
    int fd, nread;
    char buffer[MAX];
    
    fd=open("utenti.txt", O_RDONLY);
    if(fd==-1){
        perror("ERRORE APERTURA FILE");
        return top;
    }
    
    while((nread=read(fd, buffer, MAX)>0)){
        top=elabora(top,buffer);
        }
        close(fd);
    
    return top;
}


struct utente* elabora(struct utente *top, char buffer[]){
    char username[MAX], password[MAX], gettoni[MAX], stato[MAX];
    int i=0;    
    int j=0; //indice username
    int k=0; //indice password
    int t=0; //indice gettoni
    int x=0; //indice stato
    int count=0;
    
    printf("Inzio buffer:n%sfine buffern" ,buffer);
    
    while(buffer[i]!=''){
    
        //ELABORO NOME
        while(buffer[i]!=':'){
            username[j]=buffer[i];
            j  ;
            i  ;
        }
        username[j]='';
        i  ;
        
        //ELABORO COGNOME
        while(buffer[i]!=':'){
            password[k]=buffer[i];
            k  ;
            i  ;
        }
        password[k]='';
        i  ;
        
        //ELABORO GETTONI
        while(buffer[i]!=':'){
            gettoni[t]=buffer[i];
            t  ;
            i  ;
        }
        gettoni[t]='';
        i  ;
        
        while(buffer[i]!='n' amp;amp; buffer[i]!=''){
            stato[x]=buffer[i];
            x  ;
            i  ;
        }
        stato[x]='';
        
        
        if(buffer[i]=='n')
            i  ;
        
        if(buffer[i]==''){
            printf("nEnd of the buffer %cn", buffer[i]);
        printf("Fine%s %s %s %snn", username, password, gettoni, stato);
        
        top=ins_coda(top, username, password, gettoni, stato);
            return top;
        }
        
        
        printf("Utente %d, %s %s %s %snn", count, username, password, gettoni, stato);
        
        top=ins_coda(top, username, password, gettoni, stato);
        
        bzero(username, MAX);
        bzero(password, MAX);
        bzero(gettoni, MAX);
        bzero(stato, MAX);
        j=0;
        k=0;
        t=0;
        x=0;
        
    }
    
    return top;


}


struct utente* ins_coda(struct utente *t, char username[], char password[], char gettoni[], char stato[]){
    struct utente *p, *top;
    int n_gettoni;
    p=(struct utente*)malloc(sizeof(struct utente));
    
    strcpy(p->username, username);
    strcpy(p->password, password);
    n_gettoni=atoi(gettoni);
    p->gettoni=n_gettoni;
    strcpy(p->stato, stato);
    p->next=NULL;
    
    if(t==NULL)
        return p;
        
    top=t;
    while(t->next!=NULL)
        t=t->next;
        
    t->next=p;
    return top; 
}


void stampa_lista(struct utente *TOP){
    int i=1;
    while(TOP!=NULL){
        printf("Utente %d:n Username: %snPassword: %snGEttoni: %dnStato: %sn", i, TOP->username, TOP->password, TOP->gettoni, TOP->stato);
        TOP=TOP->next;
        i  ;
        }
    
    return;
}



int check(struct utente *top, char stringa[]){
    int len=strlen(stringa);
    while(top!=NULL){
        if(strncmp(top->username, stringa, len)==0){
        strcpy(top->stato, "on");
            printf("OKn");
            return 1;
        }else{
            printf("KOn");
            return 0;
        }
    top=top->next;  
    }
    return 0;
}




void aggiornamento_file(struct utente *top){
    char username[MAX], password[MAX], gettoni[MAX], stato[MAX]="";
    int fd;
    
    fd = open("utenti.txt", O_WRONLY |O_TRUNC, S_IRUSR | S_IWUSR);
            if(fd==-1){
                perror("ERROR apertura file utenti!");
                exit(1);
            }else{
                    while(top!=NULL){
                    strcpy(username, top->username);
                    strcpy(password, top->password);
                    sprintf(gettoni, "%d", top->gettoni);
                    strcpy(stato, top->stato);
                    
                    write(fd, username, strlen(username));
                        write(fd, ":", 1);
                        write(fd, password, strlen(password));
                        write(fd, ":", 1);
                        write(fd, gettoni, strlen(gettoni));
                        write(fd, ":", 1);
                        write(fd, stato, strlen(stato));
                        write(fd, "n", 1);
                        
                        top=top->next;
                    }
        }       
    close(fd);
    return;

}
 

Комментарии:

1. Это разделение строк может привести к путанице, поэтому не удобнее ли вам использовать strtok с разделителем «:» или sscanf?

2. Здесь происходит слишком много всего, чтобы дать вразумительный ответ. Во-первых, у вас есть while(buffer[i]!=''){ elabora . Может быть, я что-то упускаю, но я не знаю, где вы ожидали бы найти «». Возможно, это не имеет никакого отношения к более серьезной проблеме, но эти мелочи, вероятно, помогают скрыть ее.

3. Конечно, но я хочу понять, почему он достигает конца буфера, когда есть еще одна строка? Похоже, что он читает » » перед последней строкой файла

4. Я подозреваю '' , что именно это лежит в основе ваших char buffer[MAX]; нынешних условий.

5. Ты используешь open/read . Хотя это можно сделать, вы заново изобретаете то, что fopen/fgets уже сделано.

Ответ №1:

Если ваш файл не содержит двоичных данных и вы на самом деле не ставите 0x00 в конце a, он не должен содержать нулей для buffer[i] != '' проверки.

Строки C заканчиваются нулем, но обычно при записи в текстовые файлы этот завершающий нуль не включается. Редакторы текстовых файлов тоже этого не делают.

Одна из проблем заключается в том, что вы не инициализируете buffer переменную. Вероятно, вы случайно получаете нули, когда программа выделяет для этого 500 байт. Один из способов инициализации буфера нулями-это установить его первый элемент равным нулю при объявлении:

 char buffer[MAX] = {0,};
 

Теперь, если я правильно понял ваши сомнения, я не вижу, что конец файла происходит до того, как программа прочитает последнюю запись. Что там происходит, так это то, что программа печатает сообщение с последней строкой после того, как она напечатает сообщение о конце файла. Но программа действительно прочитала последнюю строку перед концом файла и сохранила ее в переменных, используемых printf .

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

 void get_field(char **src, char dst[MAX])
{
    int j = 0;

    while (*(*src) amp;amp; *(*src) != ':' amp;amp; *(*src) != 'n') {

        dst[j  ] = *(*src);
        (*src)  ;
    }
}

struct utente* elabora(struct utente *top, char *line, ssize_t line_len, int *count)
{
    char username[MAX] = {0,};
    char password[MAX] = {0,};
    char gettoni[MAX]  = {0,};
    char stato[MAX]    = {0,};

    get_field(amp;line, username);
    get_field(amp;line, password);
    get_field(amp;line, gettoni);
    get_field(amp;line, stato);

    printf("Utente %d, %s %s %s %snn",   (*count), username, password, gettoni, stato);

    top = ins_coda(top, username, password, gettoni, stato);

    return top;
}
 

Теперь его можно вызвать из функции, которая считывает файл по строкам с помощью geline функции:

 struct utente* lettura_file(struct utente *top)
{
    FILE  *file        = NULL;
    char  *buffer      = NULL;
    size_t buffer_size = 0;
    int count          = 0;
    ssize_t n_read     = 0;

    if ((file = fopen("utenti.txt", "r")) == NULL) {

        perror("ERRORE APERTURA FILE");
        return top;
    }

    while ((n_read = getline(amp;buffer, amp;buffer_size, file)) > 0) {

        top = elabora(top, buffer, n_read, amp;count);
    }

    if (buffer != NULL)
        free(buffer);

    if (file != NULL)
        fclose(file);

    return top;
}
 

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