Lomar Δημοσ. 12 Οκτωβρίου 2008 Δημοσ. 12 Οκτωβρίου 2008 Χαίρεται, το παρακάτω πρόγραμμα μου αναζητά (χωρίς δυναμικότητες, προσπάθησα αλλά απέτυχα - ακόμα και στο concept) μια συμβολοσειρά, εάν τη βρεί αναφέρει σε ποιά γραμμή βρέθηκε κτλ. το πρόβλημα (το οποίο και ξέρω που οφείλεται) είναι οτι εάν το υπο αναζήτηση string βρίσκεται στην αρχή κάποιας γραμμής, αυτό δε μπορεί να διαβαστεί. παραθέτω τον κώδικά μου προκειμένου να γίνω πιο κατανοητός: > #include <stdio.h> int main(){ char onoma[20]; char strfind[20]; char tempstr[20]; char temp; int vrethike=0; int line=0; int telos=0; FILE *arx; printf ("\n\n Onoma arxeiou: "); scanf("%s",onoma); printf ("\n\n Simvoloseira pros anazitisi: "); scanf ("%s", strfind); arx=fopen(onoma,"r"); if (!arx){ printf ("\n\n To arxeio den vrethike, termatismos programmatos. \n\n"); system ("pause"); return -1; } while (!feof(arx)){ temp=fgetc(arx); telos=0; while(temp!='\n' && telos!=1){ fscanf (arx, "%s", tempstr); if (strcmp(tempstr, strfind)==0){ vrethike++; printf ("\n\n Vrethike sti: %di grammi. \n\n",line+1); } telos=1; } if (temp=='\n') line++; } if (vrethike==0){ printf ("\n\n H symvoloseira: %s, den vrethike kamia fora sto arxeio: %s... \n\n", strfind, onoma); system ("pause"); } else{ printf ("\n\n Vrethike: %d fores, i sumvolosiera: %s \n\n", vrethike, strfind); system ("pause"); } fclose (arx); return 0; } το πρόβλημά μου είναι εκεί που κάνω την fgetc αφού ο η fscanf παρακάτω χάνει τον 1ο χαρακτήρα του πρώτου αλφαριθμητικού... μια άκυρη λύση που σκέφτηκα, είναι να αντιγράφω το υπο εξέταση αρχείο σε ενα πρόχειρο - νεο - αρχείο όπου μετά απο κάθε new line θα υπάρχει ένας κενός χαρακτήρας αναμέσα στην αρχή της γραμμής και του 1ου string που θα ακολουθεί. πχ το αρχικό αρχείο θα είναι: > ENA DIO TRIA TESSERA PENTE EKSI EFTA OKTO ENNIA και το νέο θα έχει την παρακάτω μορφή: > Α ENA DIO TRIA TESSERA Α PENTE EKSI EFTA Α OKTO ENNIA έτσι ώστε διατηρώντας τον παραπάνω κώδικα να κάνω τις σχετικές προσθήκες και να διαβάζεται κανονικά το υπόλοιπο κείμενο. Φυσικά στο τέλος του προγράμματος θα διαγράφεται το temp αρχείο. Αυτό που θέλω να ρωτήσω είναι αν υπάρχει κάποιο μικρό και έξυπνο τέχνασμα που να διορθώνει το bug που σας ανέφερα, έτσι ώστε να μη χρειαστεί να υλοποιήσω την 2η κουλή λύση. thnks in advance!
fromaz Δημοσ. 12 Οκτωβρίου 2008 Δημοσ. 12 Οκτωβρίου 2008 Μπορείς να προσθέσεις την εντολή fseek(arx, -1, SEEK_CUR) μετά την temp=fgetc(arx), για να πας πίσω τον file pointer. Μπορείς, επίσης, να φυλάσσεις τον πρώτο χαρακτήρα αντί στο temp στο tempstr[0] και το αποτέλεσμα του fscanf στο tempstr+1. Μια τρίτη επιλογή είναι να ξεχάσεις τις fgetc / fscanf (που έτσι κι αλλιώς δεν ενδείκνυται η εφαρμογή τους σε αυτήν την περίπτωση), και να χρησιμοποιήσεις στη θέση τους την fgets που θα σου επιτρέψει να διαβάσεις το αρχείο ανά γραμμές. Θα χρειαστεί να αλλάξεις τη strcmp με κάτι σαν τη strstr. Το καλύτερο θα ήταν να χτίσεις το προγραμματάκι γύρω από την fread, με ένα buffer 32KB, γεγονός που θα σου βελτιώσει το χρόνο εκτέλεσης μερικές εκατοντάδες φορές... Τέλος, αν θες "επαγγελματική" λύση, τότε πας σε memory mapped files, τα οποία όμως είναι εκτός standard βιβλιοθήκης. Επίσης, μπορείς να συμβουλευτείς και μερικούς advanced αλγόριθμους για string searching, όπως των Knuth, Boyer-Moore, κλπ. Υ.Γ: Αν το πρόγραμμα αυτό δεν είναι για εξάσκηση αλλά εξυπηρετεί υπαρκτή ανάγκη, μπορείς να χρησιμοποιήσεις έτοιμη λύση, όπως το grep.
ippo00 Δημοσ. 12 Οκτωβρίου 2008 Δημοσ. 12 Οκτωβρίου 2008 edit: Η ιστορία με το hopback δεν είναι απαραίτητη. Βασικά ξαναέφτιαξα το loop σε: > while(!feof(arx)) { // printf("entry"); fscanf(arx, "%s", tempstr); if(strcmp(tempstr, strfind) == 0) { vrethike++; printf ("\n\n Vrethike sti: %di grammi. \n\n",line); } // hopback = arx; if(fgetc(arx)=='\n') line++; // arx = hopback; } και δουλεύει > #include <stdio.h> #include <string.h> #include <stdlib.h> int main(int argc, char** argv) { char onoma[20]; char strfind[20]; char tempstr[20]; char temp; int vrethike=0; int line=1; int telos=0; FILE *arx; FILE *hopback; printf ("\n\n Onoma arxeiou: "); scanf("%s",onoma); printf ("\n\n Simvoloseira pros anazitisi: "); scanf ("%s", strfind); arx=fopen(onoma,"r"); if (!arx){ printf ("\n\n To arxeio den vrethike, termatismos programmatos. \n\n"); system ("pause"); return -1; } /* while (!feof(arx)) { temp=fgetc(arx); telos=0; while(temp!='\n' && telos!=1) { fscanf (arx, "%s", tempstr); if (strcmp(tempstr, strfind)==0) { vrethike++; printf ("\n\n Vrethike sti: %di grammi. \n\n",line+1); } telos=1; } if (temp=='\n') line++; } */ while(!feof(arx)) { // printf("entry"); fscanf(arx, "%s", tempstr); if(strcmp(tempstr, strfind) == 0) { vrethike++; printf ("\n\n Vrethike sti: %di grammi. \n\n",line); } // hopback = arx; if(fgetc(arx)=='\n') line++; // arx = hopback; } if (vrethike==0){ printf ("\n\n H symvoloseira: %s, den vrethike kamia fora sto arxeio: %s... \n\n", strfind, onoma); system("pause"); } else{ printf ("\n\n Vrethike: %d fores, i sumvolosiera: %s \n\n", vrethike, strfind); system ("pause"); } fclose (arx); return 0; }
Lomar Δημοσ. 12 Οκτωβρίου 2008 Μέλος Δημοσ. 12 Οκτωβρίου 2008 Μπορείς να προσθέσεις την εντολή fseek(arx, -1, SEEK_CUR) μετά την temp=fgetc(arx), για να πας πίσω τον file pointer. Μπορείς, επίσης, να φυλάσσεις τον πρώτο χαρακτήρα αντί στο temp στο tempstr[0] και το αποτέλεσμα του fscanf στο tempstr+1. Μια τρίτη επιλογή είναι να ξεχάσεις τις fgetc / fscanf (που έτσι κι αλλιώς δεν ενδείκνυται η εφαρμογή τους σε αυτήν την περίπτωση), και να χρησιμοποιήσεις στη θέση τους την fgets που θα σου επιτρέψει να διαβάσεις το αρχείο ανά γραμμές. Θα χρειαστεί να αλλάξεις τη strcmp με κάτι σαν τη strstr. Το καλύτερο θα ήταν να χτίσεις το προγραμματάκι γύρω από την fread, με ένα buffer 32KB, γεγονός που θα σου βελτιώσει το χρόνο εκτέλεσης μερικές εκατοντάδες φορές... Τέλος, αν θες "επαγγελματική" λύση, τότε πας σε memory mapped files, τα οποία όμως είναι εκτός standard βιβλιοθήκης. Επίσης, μπορείς να συμβουλευτείς και μερικούς advanced αλγόριθμους για string searching, όπως των Knuth, Boyer-Moore, κλπ. Υ.Γ: Αν το πρόγραμμα αυτό δεν είναι για εξάσκηση αλλά εξυπηρετεί υπαρκτή ανάγκη, μπορείς να χρησιμοποιήσεις έτοιμη λύση, όπως το grep. την strstr δε μπορώ να τη χρησιμοποιήσω γιατί πρόκειται για εργαστηριακή άσκηση, διαφορετικά δεν θα είχε νόημα να μην το κάνω. η fseek απ'όσο ξέρω δεν ενδείκνυται για χρήση σε text files, αλλά περισσότερο σε binary files... επίσης δοκίμασα κάτι με δυναμικότητες, αλλά δεν μου έκατσε, αυτό με τα 32κβ buffer είναι σχετικό, αφού καλύτερη θα ήταν η χρήση της sizeof, αφού πρώτα χρησιμοποιήσω τις σχετικές malloc και realloc, αλλά είναι καταστροφικό ενα τέτοιο concept δεδομένης της εργασιάς και των απαιτήσεων του καθηγητή. επίσης με την fgets δε μου "καθόταν" τπτ σωστά, αλλά η fscanf βόλεψε μια χαρά. πρόκειται για άναρχα text files, και εγώ απλά θέλω να διαβάζω ενα string τη φορά, δεν είναι οτι πρόκειται να έχουν μια συγκεκριμένη δομή, με συγκεκριμένες πληροφορίες, τις οποίες δεν θα διαβάζω σωστα. παρόλα αυτά δεν έχεις άδικο στις συμβουλές σου, σε ευχαριστώ, θα κοιτάξω καλύτερα τη 2η συμβουλή σου, η οποία είναι και πιο κοντά σε αυτό που ήδη έχω υλοποιήσει @ippo00 φίλε μου καταρχάς να σε ευχαριστήσω που έμεινες πιστός στον αρχικό μου κώδικα, το κοιτάω αυτή τη στιγμή, μόλις κατανοήσω τι ακριβώς έκανες θα σου απαντήσω! σας ευχαριστώ και τους δυο για τον χρόνο και τις γνώσεις σας EDIT μακράν πιο λιτή και λογική η λύση σου. αν κατάλαβα καλά το έβαλες πρώτα να διαβάζει τη λέξη και στη συνέχεια να ελέγχει τον επόμενο χαρακτήρα μετά τη λέξη αν είναι το newline, με τη λογική πως μετά απο κάθε λέξη ακολουθεί είτε κενό, είτε newline, είτε το τέλος του αρχείου. αν στη παραπάνω ανάλυση έχω κάνει κάποιο λάθος σε παρακαλώ να μου το επισημάνεις και αν θέλεις να μου κάνεις τη δική σου ανάλυση. σε ευχαριστώ ούτως ή άλλως
ippo00 Δημοσ. 12 Οκτωβρίου 2008 Δημοσ. 12 Οκτωβρίου 2008 Πάντως το file pointer μπορείς να το αποθηκεύεις σε ένα δεύτερο file pointer όπως είχα πάει να κάνω αρχικά με το δικό σου code. Τελικά είπα να χρησιμοποιήσω τα κενά και \n για να μην πρέπει να το κάνω rollback.
Lomar Δημοσ. 12 Οκτωβρίου 2008 Μέλος Δημοσ. 12 Οκτωβρίου 2008 ippo00 έκανα edit στο προηγούμενο post μου, αν θέλεις ρίξε του μια ματιά βασικά αυτό δεν το ήξερα, οτι δλδ τα file pointers αντιγράφονται τόσο απλά. ναι στον τελικό κώδικα έσβησα το 2ο file pointer, η μαγκιά όλη για εμένα ήταν στην αναδιάρθρωση της while, η οποία είχε σαν αποτέλεσμα και την μείωση της πολυπλοκότητας, λόγω της κατάργησης της 2η while!
ippo00 Δημοσ. 12 Οκτωβρίου 2008 Δημοσ. 12 Οκτωβρίου 2008 Κάτι που σκέφτηκα καθώς έτρωγα είναι ότι πρέπει να έχει ένα bug, άμα έχεις κένο πρίν το τέλος της σειράς τότε δεν θα αλλάξει το line counter γιατι θα πιάσει το κενό και όχι το \n. Αλλά αυτό μπορείς να το φτιάξεις νομίζω ^^
Lomar Δημοσ. 13 Οκτωβρίου 2008 Μέλος Δημοσ. 13 Οκτωβρίου 2008 το διαπίστωσα και εγώ, σκέφτηκα να αντιγράψω το αρχίκό αρχείο σε κάποιο temp αρχείο και με διαφορά ενός χαρακτήρα "μπροστά" να τροποποιήσω ως εξής τον κώδικα: > if(fgetc(arx)=='\n' && fgetc(temp==' ') line++; αλλά αυτό θα κάλυπτε μόνο τη περίπτωση όπου μεταξύ της τελευταίας (πριν του newline) λέξης και μεταξύ του newline θα μεσολαβούσε μόνο ενα κενό. μπορείς να μου δώσεις ακόμα ενα τιπ, γιατί η αντίληψη μου έχει σκαλώσει;
Επισκέπτης Δημοσ. 13 Οκτωβρίου 2008 Δημοσ. 13 Οκτωβρίου 2008 (Το παραπάνω Post το έκανα καταλάθος με το account του αδερφού μου - delete please) Μια σημαντική παρατήρηση (ίσως να είναι αυτός ο λόγος που "χάνεται" η 1η λέξη, χωρίς να έχω διαβάσει όλο τον κώδικα): >while (!feof(arx)) { temp=fgetc(arx); ... Αυτός είναι λανθασμένος τρόπος χρήσης της feof, καθώς η συνάρτηση feof διαφέρει από τις αντίστοιχες της PHP και PASCAL. ο λόγος είναι ότι για να επιστρέψει true (ή false) θα πρέπει πρώτα να έχει γίνει κάποια κλήση σε συνάρτηση I/O (πχ. fgets ή fgetc) και μετά να γίνει έλεγχος με την feof στο File pointer. Συνήθως χρησιμοποιείται για τον έλεγχο I/O errors. Ένας σωστός τρόπος να το γράψεις είναι ο εξής: EDITED: αντί για EOF είχα NULL >while ( (temp = fgetc(arx)) != EOF) { ... Για μια καλύτερη και εκτενέστερη εξήγηση διάβασε εδώ: http://www.drpaulcarter.com/cs/common-c-errors.php#4.2 Ένα ακόμα link από το C-faq: http://c-faq.com/stdio/feof.html Good Luck
Lomar Δημοσ. 13 Οκτωβρίου 2008 Μέλος Δημοσ. 13 Οκτωβρίου 2008 @DiAvOl φίλε μου πάω να το κάνω compile, έχοντας τροποποιήσει τον κώδικα όπως τον παραθέτω παρακάτω (η λούπα της while στη οποία και έγινε η αλλαγή στο βρόγχο της είναι με κόκκινο χρώμα), αλλά μου βγάζει το εξής warning ο compiler για τον βρόγχο της while: [Warning] comparison between pointer and integer > #include <stdio.h> #include <string.h> int main(void) { char onoma[20]; char strfind[20]; char tempstr[20]; char temp; int vrethike=0; int line=1; FILE *arx; printf ("\n\n Onoma arxeiou: "); scanf("%s",onoma); printf ("\n\n Simvoloseira pros anazitisi: "); scanf ("%s", strfind); arx=fopen(onoma,"r"); if (!arx){ printf ("\n\n To arxeio den vrethike, termatismos programmatos. \n\n"); system ("pause"); return -1; } [color="Red"]while ( (temp = fgetc(arx)) != NULL){ fscanf(arx, "%s", tempstr); if(strcmp(tempstr, strfind) == 0) { vrethike++; printf ("\n\n Vrethike sti: %di grammi. \n\n",line); } if(fgetc(arx)=='\n') line++; }[/color] if (vrethike==0){ printf ("\n\n H symvoloseira: %s, den vrethike kamia fora sto arxeio: %s... \n\n", strfind, onoma); system("pause"); } else{ printf ("\n\n Vrethike: %d fores, i sumvolosiera: %s \n\n", vrethike, strfind); system ("pause"); } fclose (arx); return 0; } η λύση στο πρόβλημα βρέθηκε με μια μετατροπή στον αρχικό κώδικα, του χρήστη ippo00 και λειτουργεί μια χαρά με εξαίρεση το bug της περίπτωσης όπου υπάρχει κενό/ά πριν το newline. το παραπάνω ζήτημα λοιπόν όπως είπα έχει λυθεί, αλλά προέκυψε αυτό το νεό... anyway, σε ευχαριστώ ούτως ή άλλως που ασχολήθηκες, το βραδάκι θα ρίξω μια ματιά και στα tutorial που μου πρότεινες
Επισκέπτης Δημοσ. 13 Οκτωβρίου 2008 Δημοσ. 13 Οκτωβρίου 2008 ουπς...λάθος sorry, το σωστό είναι: while ( (temp = fgetc(arx)) != EOF) -----Προστέθηκε 13/10/2008 στις 09 : 31 : 27----- Για να λύσεις το πρόβλημα σου αλλάζεις την while όπως φαίνεται παρακάτω: > while (fscanf (arx, "%s", tempstr) != EOF) { if (strcmp (tempstr, strfind) == 0) { vrethike++; printf ("\n\n Vrethike sti: %di grammi. \n\n", line+1); } while (isspace (temp = fgetc (arx))) { if (temp == '\n') line++; } ungetc (temp, arx); } Για να σου εξηγήσω τι ακριβώς συμβαίνει, έστω ότι έχουμε ένα αρχείο με τα παρακάτω περιεχόμενα (Με | συμβολίζω το σημείο που βρισκόμαστε μέσα στο File Stream) > |ENA DIO \n TRIA TESSERA PENTE \n ^Z (\n = newline char) (^Z = EOF indicator) Η συνάρτηση fscanf αγνοεί τους whitespace characters (\t,\n,space) εάν υπάρχουν στην αρχή του stream (σε αυτήν την περίπτωση δεν υπάρχουν) και διαβάζει χαρακτήρες μέχρι να συναντήσει ένα whitespace character. Δηλαδή διαβάζει το string ENA και σταματάει στο κενό, τοποθετώντας τον χαρακτήρα του κενού πίσω στο stream. Συγκρίνει το string που δίαβασε με αυτό που ψάχνουμε και αν είναι ίδια (strcmp == 0) εκτυπώνει την γραμμή αυξάνοντας παράλληλα την μεταβλητή vrethike κατα ένα. > ENA| DIO \n TRIA TESSERA PENTE \n ^Z Στην συνέχεια μπάινουμε στην εσωτερική while όπου διαβάζουμε τον επόμενο χαρακτήρα απο το stream με την συνάρτηση fgetc και τον τοποθετούμε στην μεταβλητή temp. Το loop αυτό είναι true όσο οι χαρακτήρες που διβάζουμε είναι whitespace characters (isspace(..)). Οπότε διαβάζουμε πρώτα τον χαρακτήρα του κενού στην πρώτη επανάληψη (αληθείς συνθήκη) και στην δεύτερη διαβάζουμε τον χαρακτήρα D και βγαίνουμε έξω από το loop. Με την χρήση της συνάρτησης ungetc τοποθετούμε τον χαρακτήρα D πίσω στο stream έτσι ώστε να μπορέσει η επόμενη κλήση της fscanf στην δεύτερη επανάληψη του εξωτερικού βρόγχου να διαβάσει το string DIO και να σταματήσει στο πρώτο κενό που θα συναντήσει. Ελέγχουμε πάλι εάν είναι το string Που ψάχνουμε και συνεχίζουμε. > ENA DIO| \n TRIA TESSERA PENTE \n ^Z Αυτήν την φορά ο εσωτερικός βρόγχος θα εκτελεστεί 6 φορές. Στις 4 πρώτες θα διαβάσουμε τους 4 κενούς χαρακτήρες > ENA DIO |\n TRIA TESSERA PENTE \n ^Z Στην 5η επανάληψη διβάζουμε τον χαρακτήρα αλλαγής γραμμής και αυξάνουμε την γραμμή (μεταβλητή line) κατα ένα. > ENA DIO \n |TRIA TESSERA PENTE \n ^Z Στην 6η επανάληψη διαβάζουμε τον χαρακτήρα T αλλά επειδή δεν είναι whitespace character βγάινουμε έξω από το loop και τον τοποθετούμε πίσω στο stream για να διαβάσει στην συνέχεια η συνάρτηση fscanf της εξωτερικής Loop(as) το string TRIA. Αυτά τα βήματα επαναλαμβάνονται μέχρι να διαβάσει η fscanf την τιμή EOF από το stream και να τερματιστεί ο εξωτερικός βρόγχος. Ελπίζω να βοήθησα
Lomar Δημοσ. 14 Οκτωβρίου 2008 Μέλος Δημοσ. 14 Οκτωβρίου 2008 φίλε μου βοήθησες και με το παραπάνω! βασικά το είχα σκεφτεί να κάνω έλεγχο κενού μετά απο κάθε fscanf, αλλά το πρόβλημα ήταν πως δεν ήξερα - σε περίπτωση που έβρισκα αλφαριθμητικό - πως να πάω πίσω για να διαβάσει ολόκληρο το string η fscanf, τη συνάρτηση ungetc δεν την ήξερα και όντως ήταν η λύση στο πρόβλημα αυτό. EDIT: @ippo00 φίλε μου δεν ήξερα την ungetc και είχα σκεφτεί πως για να κάνω κάτι ανάλογο θα μου έτρωγε υπερβολικό χρόνο - και κώδικα - σε σχέση με την πραγματική λύση (δλδ την έτοιμη συνάρτηση ungetc), δεν είναι οτι δεν προσπάθησα και αρκέστηκα σε προχειροδουλειά, απλά είχα σκαλώσει και δε μου έβγαινε Σας ευχαριστώ όλους για τις συμβουλές και τον χρόνο σας, σίγουρα έμαθα και κατάλαβα πολλά
Προτεινόμενες αναρτήσεις
Αρχειοθετημένο
Αυτό το θέμα έχει αρχειοθετηθεί και είναι κλειστό για περαιτέρω απαντήσεις.