Elikkäs kouluharkka aiheuttaa hieman päänvaivaa. Getline ilmeisesti saa luettua tiedostosta rivin bufferiin mutta sscanf ei ilmeisesti saa sitä luettue siitä ja sijoitettua temp:iin mikä näyttäisi aiheuttavan sitten tuon segmentation faultin mitä tuolla alkeellisella debuggerin käytöllä sain irti.
Mitenkä tuota saisi hieman parsittu workkimaan paremmin? Saisi yhden ongelman taakse niin pääsee tappelemaan seuraavien kanssa ...ja niitähän riittää....
/************************************************** * Program to convert txt-file to binary file * If conversion fails for some reason it will write * a error.txt file which you can edit and try to run this * program again to convert the fixed file to a binary file. * Usage: convert_file <input file> <output file> * More info with -h flag * **************************************************/ /* TODO: -Change the perror to use stderror -DONE -Add proper error handling -Add error procedure if txt file has errors -Is assert truly needed, can it be removed -Fix getline before sscanf | getline seems to work, sscanf doesn't -Remove the debug lines (##) from final version CURRENT STATUS: -Compiles but causes segmentation fault */ #include <stdio.h> /* fprintf */ #include <string.h> /* strerror */ #include <stdlib.h> /* malloc, free, exit */ #include <assert.h> /* assert for debugging */ #include <errno.h> /* errno */ #include <sys/types.h> /* getline */ /**********************/ /* MACROS */ #define BUFFER 512 /* Define default buffer size */ #define DEBUG 1 /* Verbose output, enables the DEBUG lines */ /*********************/ /* GLOBAL VARIABLES */ extern int errno; /* Error handling */ /**********************/ /* STRUCTS */ struct Data { char license[6]; /* Car license */ char manufacturer[BUFFER]; /* Manufacturer of the car */ char model[BUFFER]; /* Model of the car */ int year; /* Year the car was made */ float driven; /* How much the car is driven for in km */ char notice[BUFFER]; /* Other info, has the car been checked etc. */ struct Data *next; /* Pointer to next struct on the list */ }; struct Data *temp, *info, *head, *tptr; /* Pointers needed by linked list */ /**********************/ /* FUNCTIONS */ int print_help() { printf("\tConvert_file <input file> <output file>\n \ Program to convert txt-file to binary file\n \ If conversion fails for some reason it will write\n \ a error.txt file which you can edit and try to run this\n \ program again to convert the error to a binary file.\n\n \ Usage: convert_file -c <input file> <output file>\n\n \ Available command line switchs:\n"); printf("\n\t-c (C)onvert <file1> to <file2>: Converts ASCII file to binary file\n \ \t-f (F)ix <file>: Reads error.txt and writes it to output file if OK\n\n"); return 0; } char * fix_buffer(char *buffer) /* Function that tries to fix the buffer input */ { /* Check the buffer string and try to figure what is the problem in it */ /* Case 1: Too few arguments */ /* Case 2: Too much arguments*/ /* Case 3: Wrong type of arguments */ /* Case 4: Missing delimiter on line */ /* Case 5: Empty line on middle of file */ /* Case 6: */ return buffer; } /**********************/ /* MAIN */ int main(int argc, char *argv[]) { int i; /* Index variable for loops */ FILE *file1,*file2; /* Define input file (fp) and output file (fp2) */ char *buffer; /* Buffer string where to check and modify data */ int return_value; /* Variable to hold our return values */ size_t len = 0; ssize_t read; /* Run through command arguments and */ /* check if some switches were given */ for(i=1;i<argc;i++) { if(argv[i][0] == '-') { switch(argv[i][1]) { case 'h' : print_help(); exit(0); case 'f' : break; default : printf("Unknown parameter %s given\n",argv[i]); exit(1); } } } /* If no switches found check that */ /* there's enough parametres given to program */ if(argc<3) { printf("%s -hf <file1> <file2>\n",argv[0]); exit(1); } /* Try to open txt file for reading and if not succesfull print error */ if (DEBUG) { assert((file1=fopen(argv[1],"r"))); } else { if ((file1=fopen(argv[1],"r"))==NULL) { fprintf( stderr, strerror( errno ) ); exit(EXIT_FAILURE); } } /* Set pointer values to NULL */ printf("\n##Setting temp->next to NULL"); //temp->next = NULL; printf("\n##Setting info->next to NULL"); //info->next = NULL; printf("\n##Setting head->next to NULL"); //head->next = NULL; printf("\n##Setting tptr->next to NULL"); //tptr->next = NULL; /* <<<< READING OF FILE1 BEGINS >>>>*/ read = 0; return_value = 0; /* Start looping the file until EOF is reached */ while(!feof(file1)) { /* Read line from file to buffer */ read = getline(&buffer, &len, file1); /* returns -1 if fails */ printf("## Buffer is: %s\nlen is: %d\n",buffer,len); if (read == -1) { printf("Couldn't read line from file to buffer!\nRead = %d",read); exit(EXIT_FAILURE); } /* Try to get 6 parameters from the buffer or try to cope with error*/ return_value == sscanf(buffer, "%5s,%511s,%511s,%d,%f,%511s",temp->license, temp->manufacturer, temp->model, &temp->year, &temp->driven, temp->notice); printf("##Returnvalue before if-statement: %d\n",return_value); printf("##Temp values: %s %s %s %d %f %s",temp->license, temp->manufacturer, temp->model, temp->year, temp->driven, temp->notice); printf("##Give temp return value instead of %d: ", return_value); scanf("%d",&return_value); if (return_value == 6) { /* Change to switch-case structure? */ info = malloc(sizeof *info); /* Is following correct? */ if (info == NULL) { fprintf(stderr, strerror (errno) ); exit(EXIT_FAILURE); } printf("##Temp values before moving: %s,%s,%s,%d,%f,%s\n", temp->license,temp->manufacturer,temp->model,temp->year,temp->driven,temp->notice); /* Copy values from temp to info */ info = temp; printf("##Info values after moving: %s,%s,%s,%d,%f,%s\n", info->license,info->manufacturer,info->model,info->year,info->driven,info->notice); /* If this is the first info struct point head pointer to it... */ if(!head) { head = info; tptr = info; } /* ...otherwise put the struct after */ /* the last element in the list */ else { tptr->next = info; tptr = info; } /* If error occures in file, print it and exit ??? IS THIS NEEDED?*/ if(ferror(file1)) { fprintf(stderr, strerror(errno) ); exit(EXIT_FAILURE); } } /* <<<< IF FSCANF FAILS >>>>*/ else { /* Try to fix things, try to read values and put empty values instead */ printf("Sscanf return value is %d", return_value); printf("Error while reading data from %s .\nTrying to fix it...", argv[1]); //char *new_buffer[BUFFER]; //new_buffer = fix_me(buffer); /* Read line and write it in ASCII to error file for inspection */ } } /* Close file1 after use*/ fclose(file1); /* <<<< WRITING TO FILE2 BEGING >>>>*/ /* Try to open file2 for binary write. */ /* If error is found, print it and exit */ if (DEBUG) { assert((file2=fopen(argv[1],"wb"))); } else { if ((file2=fopen(argv[1],"wb"))==NULL) { fprintf(stderr,strerror(errno)); exit(EXIT_FAILURE); } } /* Set temp pointer to head so */ /* following list manipulations will succeed */ tptr = head; /* Write info structs from the linked list to file and free unused structs */ /* Do while not in last item (Will it work for last item?) */ while (head->next) { if(DEBUG) { assert(fwrite(&info, sizeof info, 1, file2)); } else { fwrite(&info, sizeof info, 1, file2); /* Check return value for success */ } /* Move head pointer to next item and then free the old head item*/ head = head->next; free(tptr); tptr = head; printf("##Writing following to file: %s,%s,%s,%d,%f,%s\n", info->license,info->manufacturer,info->model,info->year,info->driven,info->notice); if(ferror(file1)) { fprintf(stderr,strerror(errno)); exit(EXIT_FAILURE); } } /* Closing both files after use*/ fclose(file2); return (EXIT_SUCCESS); /* End main */ }
GNU Debuggerilla kokeilin katsoa hieman segmentation faultin syytä ja seuraavaa antaa.
(gdb) run re re2 Starting program: /home/xxx/ohjelmointi/alg/harkka re re2 ##Setting temp->next to NULL ##Setting info->next to NULL ##Setting head->next to NULL ##Setting tptr->next to NULL## Buffer is: baa-sdr#Volvo#Seat#2004#432111#checked nbytes is: 120 ##Returnvalue before if-statement: -1208594444 Program received signal SIGSEGV, Segmentation fault. 0x08048978 in main (argc=3, argv=0xbffc5094) at harkka1.c:163 163 printf("##Temp values: %s %s %s %d %f %s",temp->license, temp->manufacturer, temp->model, temp->year, temp->driven, temp->notice);
Auttaisi, jos antaisit vielä minimaalisen mallitiedoston sekä käyttöesimerkin, ei jaksaisi sellaisia tuosta koodista kaivaa.
Monen rivin mittaisiin teksteihin suosittelisin selkeyden vuoksi lainausmerkkejä joka riville ja \-merkin poistoa rivien lopuista.
printf("Rivi 1\n" "Rivi 2\n"); // Sama kuin printf("Rivi 1\nRivi 2\n");
Kooditageiksi suosittelisin C-kooditageja (koodic). Koodin rivejä voisi myös vähän yrittää välittää pilkkujen ja muiden operaattorien kohdalta, nytkin tuolla on jokin aika pitkä pätkä ilman yhtään väliä, meinaa sivun leiska hajota.
Edit.
Yksinkertaisella tutkimuksella tuosta sai kuin saikin irti muutaman ongelman ainakin. Ilmeisesti temp-muuttujalle ei ole varattu tilaa lainkaan, olenko oikeassa? Silti siihen yritetään tallentaa arvoja. Pitäisikö sen ehkä olla oikea struct eikä osoitin? Lisäksi sijoitat info = temp
, mikä siis tarkoittaa osoitteiden, ei sisältöjen sijoittamista. Käytä memcpy-funktiota: memcpy(kohde, lahde, sizeof(*kohde));
. Lisäksi linkitetyssä listassasi taitaa olla sellainen pulma, että malloc
ei alusta muistia nollaksi, jolloin head->next
ei välttämättä koskaan saavuta nollaa. Alusta siis info
-muuttujaa varatessasi sen next
-kenttä nollaksi.
Debuggerin käyttöön vielä sellaisia ohjeita, että kun ohjelman kääntää debuglipulla -g
, ainakin minulla gdb kertoo aika nätisti, millä rivillä ohjelma kaatui. Arvoja saa tulostettua komentamalla print muuttuja
.
Eli homma pitäisi muuttaa seuraavanlainen syöte:
baa-234#Volvo#Seat#2004#432111#checked fas-324#Nissan Primera#2005#23444#checked
Lukea tuollaisia rivejä txt-filusta, siirtää nämä tietueeksi ja tallettaa binaarisenä tietueet toiseen filuun.
Kiitos tuosta printf -vinkistä. Pitää päivitellä sen mukaan tuota.
En muistanut noita tarkkoja kooditageja kun en ole vähään aikaan postitellut tänne :) Pistetään korvan taakse.
Jees, kiitoksia noista vinkeistä. On taas katseltu liian pitkään tuota samaa rimpsua kuin ei osaa ajatella noin yksinkertaisia seikkoja kuntoon.
Pitää kyllä tuo debuggerin käyttö opetella oikein kantapään kautta kunhan siihen on aikaa. Auttaisi aika paljon ohjelmoinnissa kuitenkin.
Ai niin, toinen ongelmasi on ihan tuo scanf:n formaatti. Eihän sieltä voi pilkkuja lukea tekstien välissä, jos siellä on # eikä pilkku. Lisäksi %s pysähtyy ennen maksimipituutta vain välilyöntiin tai vastaavaan, joten formaattisi ei sikälikään toimi. Kokeilepa jotain tällaista:
sscanf(puskuri, "%5[^#]#%511[^#]", s1, s2);
Tällä siis luetaan kahteen kertaan mitä tahansa merkkejä paitsi # ja tietenkin niiden välistä se # pois. Tekstien lopetusmerkit täytyy itse asettaa, helpointa on tietenkin etukäteen asettaa koko teksti nolliksi. (Nolliksi alustetun tietueen voi varata calloc
-funktiolla, calloc(1, koko)
.)
Tuo 5 (6) merkkiä on sitä paitsi liian vähän rekisterikilvelle, pitäisi kelpuuttaa 7 (8) merkkiä jo Suomessakin.
Tämän siitä saa kun on koodannut vain C++:aa eikä ole koskenut scanf:iin kuin pikaisesti.
Pitää kokeilla paikkailla tuota huomenissa. Ja minun maailmassa rekisterit ovat edelleen 6 merkkiä :)
Zmyrgel kirjoitti:
Ja minun maailmassa rekisterit ovat edelleen 6 merkkiä :)
Mikähän se viiva sitten on? ;)
Tehdäänpä aiheesta empiirinen koe:
const char rek[] = "abc-123"; printf("Rekisterin %s pituus on %d merkkiä.\n", rek, strlen(rek)); // abc-123 pituus on 7 merkkiä. // 1234567
... koodaan sillein että se viiva tulee vain tulostus vaiheessa näkyviin niin sitten se on 6 merkkiä :)
Jees, sscanfin kanssa edelleen probleema. On kokeiltu hieman eri tapoja homman hoitoon mutta aina vaan tuntuu menevän syvemmälle ojaan ratkaisuissa. Tätä nykyä ohjelmasta on karsittu tuo linkitetty lista kokonaan, jos sscanf lukee kaikki arvot niin struct kirjoitetaan binaari tiedostoon muuten virhetiedostoon.
Getline funktio näyttäisi ainakin saavan luettua arvot bufferiin ihan moitteetta.
Voisiko joku hieman valottaa tuota sscanfin [^#] toimintaa kun netistäkin suht rajoitetusta tuosta löysin infoa ja sen pienen minkä löysin niin ei valottanut asiaa yhtään.
Nyt tuo sscanf näyttää tältä:
sscanf(buffer, "%s[^#]#%s[^#]#%s[^#]#%d[^#]#%f[^#]#%s",info.license, info.manufacturer, info.model, &info.year, &info.driven, info.notice);
Myöskin seuraavat varoitukset askarruttavat:
Getline funktio antaa kääntäessä seuraavaa:
implicit declaration of function 'getline
ja info structin alustus:
data info = {{'\0','\0','\0',0,0.0,'\0',NULL}};
Antaa: initialization makes integer from pointer without cast.
Jeh, sain nuo varoitukset ohjelmasta pois käyttämällä memset:iä tietueen alustukseen ja lisäämällä rivin "#define _GNU_SOURCE" koodin alkuun.
Vielä kun sscanf pelittää niin ollaan jo voiton puolella.
Edit: homma toimii nyt siihen malliin että saan tuohon license kenttään luettua ihan oikean arvon enkä mitään ylimääräistä mutta sitten muuta kentät jäävät tyhjiksi. Taikka sitten saan luettua kaikkiin string kenttiin koko bufferin sisällön mutten mitään noihin numereellisiin kenttiin.
Vilkaisepa esimerkkiäni uudestaan, %[^#]
ei kaipaa s
:ää (eikä sitä kuulu tietenkään liittää %d:henkään).
Luulen, että alustuksessasi olet alustamassa license-tekstiä etkä koko tietuetta, kun kerran on kaksinkertaiset sulut. Tietueen alustuksen pitäisi toimia millä tahansa näistä:
data info = {{0}}; // Lyhyin ja varmin data info = {"", "", "", 0, 0.0, "", NULL}; data info = {{0}, {0}, {0}, 0, 0.0, {0}, NULL};
Jees, nyt homma ohjelman perustoiminnot näyttää toimivan kuten pitääkin. Pitää vielä lisätä nuo error handling toiminnot kehiin niin alkaa olla homma kuosissa. Kiitoksia paljon avusta.
Aihe on jo aika vanha, joten et voi enää vastata siihen.