#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
было бы лучше, чем разбирать токены таким образом. Использование других функций для открытия и чтения файла, как указано в комментариях к вопросам, также улучшит код.