קבצים קרן כליף © Keren Kalif ביחידה זו נלמד: מוטיבציה לעבודה עם קבצים סוגי קבצים: פעולות על קבצים (עבור שני סוגי הקבצים): 2 בינאריים טקסט פתיחת קובץ קריאה מקובץ כתיבה לקובץ סגירת קובץ פעולות נוספותfseek, ftell, rewind, ferror, fflush, feof : © Keren Kalif קבצים -מוטיבציה כאשר אנחנו כותבים תוכנה ,המידע אשר אנחנו מכניסים בכל הרצה הולך לאיבוד עם סיומה ,מאחר והתוכנה רצה בזיכרון ה RAM -של המחשב היינו רוצים לשמור את המידע בין ההרצות בזיכרון הקבוע ( )Hard Diskכדי: ליצור תוכנה בעלת משמעות ורצף להקל עלינו בעת בדיקות התוכנה 3 קריאת נתונים מקובץ מוכן מראש ולא מהמשתמש שמירת נתונים לשימוש ע"י תוכניות אחרות ניתן לשמור את המידע בכמה אופנים שונים: ( DB לא יילמד בקורס זה) קבצים © Keren Kalif עבודה כללית עם קבצים -מוטיבציה 4 יתכן ותהייה לנו תוכנית שנרצה לשמור את הפלט שלה לקובץ יתכן ותהייה לנו תוכנית שנרצה שהקלט שלה יהיה מקובץ, ולא מהמקלדת דוגמא: קריאת אוסף מספרים מקובץ בפורמט ידוע מראש והצגת סכום המספרים כתיבת תוכנית המכינה דו"ח כלשהו ושומרת את הדוח בקובץ ,בנוסף להצגתו על המסך © Keren Kalif סוגי קבצים 5 קובץ טקסט: כתוב בשפה אותה אנו יכולים לקרוא ולהבין קובץ בינארי: לא ניתן להבין את התוכן © Keren Kalif מבנה עבודה כללי עם קבצים קובץ הוא משתנה מטיפוס * FILEומוגדר בstdio.h - בתחילת העבודה יש לפתוח קובץ לקריאה/לכתיבה תוך ציון האם זהו קובץ טקסט או בינארי יש לבדוק האם פתיחה הצליחה לבסוף יש לסגור את הקובץ )(void main { ;)>FILE* f = fopen(<file name>, <open parameters )if (f == NULL { ;)"printf("Failed opening the file. Exiting!\n ;return } // here some operation with the file.. ;)fclose(f } 6 1. 2. 3. 4. 5. 6. 7. 8. 9. 10. 11. © Keren Kalif פתיחת קובץ – פרמטרים לסוג הפתיחה ;)FILE* fopen(char* fileName, char* mode פתיחת קובץ טקסט לקריאה“r” : פתיחת קובץ טקסט לכתיבה: פתיחת קובץ טקסט לקריאה ולכתיבה: ” – “r+הקובץ חייב להיות קיים ,יתקבל NULLאם לא ” – “w+אם הקובץ קיים ,דורס אותו ” – “a+אם הקובץ קיים כותב לסופו ,אחרת יוצר קובץ חדש .לא מאפשר כתיבה לתחילת הקובץ! כדי לפתוח את הקובץ כבינארי נוסיף bבסוף הפרמטר לפתיחה. למשל “rb”, “wb” :וכו' .ברירת המחדל היא קובץ טקסט. 7 ” - “wבמידה ויש נתונים בקובץ הם ידרסו ” – “aכותב לסוף קובץ (כלומר לא דורס את המידע ,אם קיים) .לא מאפשר כתיבה לתחילת הקובץ! ניתן ב mode -הפתיחה לציין ,tעבור קובץ טקסט .למשל“wt” : © Keren Kalif פתיחת קובץ – סיבות לכשלון 8 קובץ המוגדר לקריאה בלבד שמנסים לפתוח אותו לכתיבה פתיחה ע"י " "rקובץ שאינו קיים © Keren Kalif סגירת קובץ ;) int fclose(FILE* file מקבלת מצביע לקובץ ומחזירה 0אם הצליחה לסגור אותו, אחרת תחזיר ( -1הקבוע )EOF 9 תכשל למשל כאשר מנסים לסגור קובץ שאינו פתוח ;)( int fcloseall סוגרת את כל הקבצים שפתחנו ומחזירה את מספר הקבצים שסגרה © Keren Kalif כתיבה לקובץ טקסט פקודות הכתיבה לקובץ טקסט הן כמו הפקודות לכתיבה למסך ,פרט לכך ש: שמן מתחיל עם f הן מקבלות פרמטר נוסף שהוא מצביע לקובץ אליו רוצים לכתוב ;) int fprintf(FILE* , char*, ... ;)* int fputs(char*, FILE ;)* int fputc(char, FILE 10 © Keren Kalif )1( כתיבה לקובץ טקסט – דוגמא 1. 2. 3. 4. 5. 6. 7. 8. 9. 10. 11. 12. 13. 14. 15. 16. 17. 18. #include <stdio.h> void main() { FILE* f = fopen("myFile.txt", "w"); int res; if (f == NULL) דריסת הקובץ אם קיים { printf("Failed opening the file. Exiting!\n"); return; } fprintf(f, "%s %d %lf\n", "KerenK", 28, 99.8); fputs("Hello World!", f); fputc('A', f); fclose(f); } 11 © Keren Kalif )2( כתיבה לקובץ טקסט – דוגמא 1. #include <stdio.h> 2. 3. 4. 5. 6. 7. 8. 9. 10. 11. 12. 13. void main() { FILE* f = fopen("myFile.txt", "w"); int res; if (f == NULL) { printf("Failed opening the file. Exiting!\n"); return; } fputs("Hello World!\n", f); fclose(f); 14. f = fopen("myFile.txt", "a"); fputs("And Good Morning!\n", f); fclose(f); 15. 16. 17. 18. פתיחת הקובץ וכתיבה לסופו } 12 © Keren Kalif )3( כתיבה לקובץ טקסט – דוגמא 1. #include <stdio.h> 2. 3. 4. 5. 6. 7. 8. 9. 10. 11. 12. 13. void main() { FILE* f = fopen("myFile.txt", "w"); int res; if (f == NULL) { printf("Failed opening the file. Exiting!\n"); return; } fputs("Hello World!\n", f); fclose(f); ...דריסת הקובץ הקודם 14. f = fopen)"myFile.txt", “w"); fputs("And Good Morning!\n", f); fclose(f); 15. 16. 17. 18. } 13 © Keren Kalif קריאה מקובץ טקסט פקודות הקריאה לקובץ טקסט הן כמו הפקודות הקריאה למסך ,פרט לכך ש: שמן מתחיל עם f הן מקבלות פרמטר נוסף שהוא מצביע לקובץ אליו רוצים לכתוב מקבלות גם את כמות התווים לקריאה. קוראת עד ’ ‘\nאו עד n-1תווים ;) int fscanf(FILE* , char*, ... ;)*char* fgets(char*, int n, FILE מחזירה את המחרוזת שקראה או NULLאם לא הצליחה (למשל הקובץ נגמר) ;)*int fgetc(FILE מחזירה את ערך האסקיי של התו או את הקבוע ( EOFשערכו )-1אם לא הצליחה (למשל הקובץ נגמר) 14 © Keren Kalif שמירה וקריאה מקובץ -כללי 15 כאשר נשמור מידע לקובץ ,אנו בוחרים לשמור את המידע בסדר מסוים צריכה להיות התאמה בין סדר כתיבת הנתונים לקובץ לסדר קריאת הנתונים מהקובץ למשל :אם כתבתי מידע על סטודנט בסדר הבא :שם ,ת.ז. וגיל ,גם סדר קריאת הנתונים יהיה זהה כלומר ,צריכה להיות הסכמה וידיעה בין מי שכותב לקובץ ומי שקורא ממנו לגבי סדר הנתונים בקובץ © Keren Kalif קריאה מקובץ טקסט – דוגמא 1. 2. 3. 4. 5. 6. 7. 8. #define SIZE 20 void main() { char str[]="Hi", sentence[]="Hello World!", str2[SIZE], sentence2[SIZE]; int num1=6, num2=0; float f1=4.5, f2=0; FILE* f = fopen("myFile.txt", "w"); // check if open file succeeded.. 9. fputs(sentence, f); fprintf(f, "\n%d %f %s\n", num1, f1, str); fclose(f); 10. 11. 12. 13. printf("Before reading from file:\nnum2=%d f2=%f str=|%s| sentence2=|%s|\n", num2, f2, str2, sentence2); f = fopen("myFile.txt", "r"); // check if open file succeeded.. 14. 15. 16. 17. fgets(sentence2, SIZE, f); fscanf(f, "%d %f %s", &num2, &f2, str2); 18. 19. 20. printf("\nAfter reading from file:\nnum2=%d f2=%f str=|%s| sentence2=|%s|\n", num2, f2, str2, sentence2); fclose(f); 21. 22. 23. } 16 © Keren Kalif feof ;)int feof (FILE * stream 17 פונקציה זו מקבלת קובץ פתוח ומחזירה 0אם לא הגענו לסוף הקובץ ,ו )EOF( -1 -במידה והגענו לסוף © Keren Kalif דוגמא – העתקת קובץ 1. 2. 3. 4. 5. 6. 7. 8. 9. 10. 11. 12. 13. 14. 15. 16. 17. 18. 19. 20. void main() { char ch; FILE* fSource, *fDest; fSource = fopen("example.txt", "r"); // check if open file succeeded.. fDest = fopen("exampleCopy.txt", "w"); // check if open file succeeded.. ch = fgetc(fSource); while (!feof(fSource)) // OR: (ch != EOF) { fputc(ch, fDest); ch = fgetc(fSource); } fclose(fSource); // returns 0 since close file succeed fclose(fDest); // returns 0 since close file succeed } fcloseall(); // returns 2 since closed 2 files 18 © Keren Kalif קריאת מספרים מקובץ וחישוב הממוצע 1. 2. 3. 4. 5. 6. 7. 8. 9. 10. 11. 12. 13. 14. 15. 16. 17. 18. 19. 20. #include <stdio.h> void main() { FILE* f; int num, sum=0, counter=0, rc; f = fopen("numbers.txt", "r"); // check if open succeed... rc = fscanf(f, "%d", &num); while (rc != EOF) //OR: (!feof(f)) { printf("The read numbers is %d\n", num); sum += num; counter++; rc = fscanf(f, "%d", &num); } fclose(f); printf("The average is %f\n", (float)sum/counter); } 19 © Keren Kalif שמירת וטעינת רשומות לקובץ טקסט 20 כאשר כותבים רשומה לתוך קובץ טקסט ,יש לכתוב שדה-שדה כאשר קוראים רשומה מקובץ טקסט ,יש לקרוא שדה-שדה © Keren Kalif שמירת וטעינת רשומות דוגמא- מקובץ טקסט 1. 2. 3. 4. 5. 6. 7. 8. 9. 10. 11. 12. 13. 14. #define SIZE 20 struct Person { char name[SIZE]; long id; float age; } typedef person_t; void main() { person_t p1={"momo", 1111, 23.5}, p2 = {"gogo", 2222, 24.8}, p3, p4; FILE* f = fopen("persons.txt", "w"); fprintf(f, "%s %ld %.2f\n", p1.name, p1.id, p1.age); fprintf(f, "%s %ld %.2f\n", p2.name, p2.id, p2.age); fclose(f); f = fopen("persons.txt", "r"); fscanf(f, "%s %ld %f\n", p3.name, &p3.id, &p3.age); fscanf(f, "%s %ld %f\n", p4.name, &p4.id, &p4.age); fclose(f); 15. 16. 17. 18. printf("p3: name: %s\t id: %ld\t age: %.2f\n", p3.name, p3.id, p3.age); printf("p4: name: %s\t id: %ld\t age: %.2f\n", p4.name, p4.id, p4.age); 19. 20. 21. } 21 © Keren Kalif שמירה וטעינת מערך של רשומות מקובץ טקסט 22 כאשר כותבים מערך לקובץ ,השדה הראשון צריך להיות מספר הרשומות שאנו כותבים ,כדי שקורא הקובץ ידע כמה רשומות יש בתוכו © Keren Kalif )1( מערך רשומות- שמירה וטעינה 1. 2. 3. 4. 5. 6. struct Person { char name[SIZE]; long id; float age; } typedef person_t; 7. 8. 9. 10. 11. 12. 13. 14. void main() { FILE* f; person_t* personsSource, *personsDest; int sizeSource, sizeDest, i; printf("How many persons? "); scanf("%d", &sizeSource); 15. // allocating the persons array personsSource = (person_t*)malloc(sizeSource*sizeof(person_t)); // reading persons.. for (i=0 ; i < sizeSource ; i++) { printf("Enter name, id and age of person #%d: ", i+1); scanf("%s %ld %f", personsSource[i].name, &personsSource[i].id, &personsSource[i].age); } 16. 17. 18. 19. 20. 21. 22. 23. 24. } 23 © Keren Kalif 24. 25. 26. 27. 28. f = fopen("persons.txt", "w"); fprintf(f, "%d\n", sizeSource); // writing the size to the file for (i=0 ; i < sizeSource ; i++) // writing each person to the file fprintf(f, "%s %ld %f\n", personsSource[i].name, personsSource[i].id, personsSource[i].age); fclose(f); 29. 30. free(personsSource); // don’t forget to free the array!! 31. 32. 33. 34. 35. 36. 37. 38. 39. f = fopen("persons.txt", "r"); fscanf(f, "%d\n", &sizeDest); // reading the size from the file // allocating the new array personsDest = (person_t*)malloc(sizeDest*sizeof(person_t)); // reading each person from the file for (i=0 ; i < sizeDest ; i++) fscanf(f, "%s %ld %f\n", personsDest[i].name, &personsDest[i].id, &personsDest[i].age); fclose(f); 40. 41. 42. 43. 44. printf("There are %d persons in the file:\n", sizeDest); for (i=0 ; i < sizeDest ; i++) printf("Person #%d: Name=%s\t Id=%ld\t Age=%f\n", i+1, personsDest[i].name, personsDest[i].id, personsDest[i].age); 45. 46. 47. } free(personsDest); // don’t forget to free the array!! 24 © Keren Kalif כתיבה לקובץ בינארי פקודת הכתיבה לקובץ בינארי: int fwrite(const void * ptr, // address of variable to write int size, // size of type int count, // number of elements FILE * stream( ; // pointer to the file מחזירה את כמות האיברים שכתבה כאשר כותבים לקובץ בינארי ניתן לכתוב בלוק של מידע 25 למשל :מערך או רשומה בפעולת כתיבה בודדת יש להיזהר מכתיבת פוינטרים לקובץ! © Keren Kalif קריאה מקובץ בינארי פקודת הקריאה מקובץ בינארי: int fread(void* ptr, // address of variable to read into int size, // size of type int count, // number of elements FILE* stream( ; // pointer to the file מחזירה את כמות האיברים שקראה כאשר קוראים מקובץ בינארי ניתן לקרוא בלוק של מידע 26 למשל :מערך או רשומה בפעולת קריאה בודדת © Keren Kalif דוגמא- שמירת וטעינת רשומות מקובץ בינארי 1. 2. 3. 4. 5. 6. 7. #define SIZE 20 struct Person { char name[SIZE]; long id; float age; } typedef person_t; 8. 9. 10. 11. 12. 13. 14. 15. void main() { person_t p1={"momo", 1111, 23.5}, p2 = {"gogo", 2222, 24.8}, p3, p4; FILE* f = fopen("persons.bin", "wb"); fwrite(&p1, sizeof(person_t), 1, f); fwrite(&p2, sizeof(person_t), 1, f); fclose(f); !ניתן לקרוא ולכתוב רשומה בפעולה אחת 16. f = fopen("persons.bin", "rb"); fread(&p3, sizeof(person_t), 1, f); fread(&p4, sizeof(person_t), 1, f); fclose(f); 17. 18. 19. 20. 21. printf("p3: name: %s\t id: %ld\t age: %.2f\n", p3.name, p3.id, p3.age); printf("p4: name: %s\t id: %ld\t age: %.2f\n", p4.name, p4.id, p4.age); 22. 23. 24. } 27 © Keren Kalif שמירה וטעינה מערך רשומות )1( מקובץ בינארי 1. 2. 3. 4. 5. 6. 7. #define SIZE 20 struct Person { char name[SIZE]; long id; float age; } typedef person_t; 8. 9. 10. 11. 12. 13. 14. 15. void main() { FILE* f; person_t* personsSource, *personsDest; int sizeSource, sizeDest, i; printf("How many persons? "); scanf("%d", &sizeSource); 16. 17. 18. 19. 20. 21. 22. 23. 24. // allocating the persons array personsSource = (person_t*)malloc(sizeSource*sizeof(person_t)); for (i=0 ; i < sizeSource ; i++) // reading persons.. { printf("Enter name, id and age of person #%d: ", i+1); scanf("%s %ld %f", personsSource[i].name, &personsSource[i].id, &personsSource[i].age); } 28 © Keren Kalif 25. 26. 27. 28. 29. 30. f = fopen("persons.bin", "wb"); fwrite(&sizeSource, sizeof(int), 1, f); // writing the size to the file // writing all persons to the file fwrite(personsSource, sizeof(person_t), sizeSource, f); fclose(f); free(personsSource); // don’t forget to free the array!! 31. 32. 33. 34. 35. 36. 37. 38. 39. f = fopen("persons.bin", "rb"); // reading the size from the file fread(&sizeDest, sizeof(int), 1, f); // allocating the new array personsDest = (person_t*)malloc(sizeDest*sizeof(person_t)); // reading all persons from the file fread(personsDest, sizeof(person_t), sizeDest, f); fclose(f); 40. 41. 42. 43. 44. printf("There are %d persons in the file:\n", sizeDest); for (i=0 ; i < sizeDest ; i++) printf("Person #%d: Name=%s\t Id=%ld\t Age=%f\n", i+1, personsDest[i].name, personsDest[i].id, personsDest[i].age); 45. 46. 47. 48. } // don’t forget to free the array!! free(personsDest); 29 © Keren Kalif כתיבת רשומות המכילות מצביעים יש לשים לב לא לכתוב מצביעים לתוך קובץ ,שכן אין הבטחה שבפעם הבאה שהתוכנית תרוץ יהיו ערכים בכתובות הנוכחיות שאנו שומרים לכן ,כאשר יש משתנה שהוא כתובת יש להקפיד לרשום את ערכו לקובץ 30 נשים לב :לא נוכל להשתמש בשיטה של לרשום רשומה ע"י ,fwrite אלא נהיה חייבים לכתוב את הרשומה לקובץ שדה-שדה בד"כ אם המצביע הוא למערך ,נרשום כשדה נוסף את כמות האלמנטים במערך ,ורק אח"כ את איבריו (כנ"ל עבור מחרוזת). © Keren Kalif דוגמא – שמירה וטעינה של רשומה עם מצביעים 1. 2. 3. 4. 5. 6. 7. 8. #include <stdio.h> #include <string.h> #include <stdlib.h> struct Student { char* name; float average; } typedef student_t; 9. void main() 10. { 11. student_t stud1 = {"yoyo", 91.8}, stud2; 12. FILE* f = fopen("students.bin", "wb"); 13. int len1, len2; 14. // check open succeed... 15. 16. len1 = strlen(stud1.name)+1; 17. fwrite(&len1, sizeof(int), 1, f); 18. fwrite(stud1.name, sizeof(char), len1, f); 19. fwrite(&stud1.average, sizeof(float), 1, f); 20. fclose(f); 21. 22. f = fopen("students.bin", "rb"); 23. // check open succeed... 24. 25. fread(&len2, sizeof(int), 1, f); 26. stud2.name = (char*)malloc(len2*sizeof(char)); 27. fread(stud2.name, sizeof(char), len2, f); 28. fread(&stud2.average, sizeof(float), 1, f); 29. fclose(f); 30. printf("stud2: name=%s, average=%.2f\n", 31. stud2.name, stud2.average); 32. free(stud2.name); 33. } 31 © Keren Kalif פקודה fseek מאפשר לטייל בקובץ בלי לקרוא חלק משדותיו int fseek(FILE* f, // the file long int offset, // how much ; (int origin // from where originיקבל אחד מהערכים הבאים: 32 – SEEK_SETלזוז מתחילת הקובץ – SEEK_ENDלזוז לסוף הקובץ – SEEK_CURלזוז מהמיקום הנוכחי הפונקציה תחזיר 0אם הצליחה לזוז כמתבקש לידע כללי :יש מערכות הפעלה בהן fseekלא עובדת טוב על קבצי טקסט © Keren Kalif דוגמא- fseek 1. #include <stdio.h> 2. 3. 4. 5. 6. 7. int main () { FILE * f; f = fopen("myfile.txt" , "w"); // check if open succeed 8. fputs("This is an apple" , f); fseek (f , 9 , SEEK_SET); fputs(" sam" , f); fclose(f); 9. 10. 11. 12. 13. } 33 © Keren Kalif – דוגמא – החבר הכי טובfseek 1. #define MAX_LEN 20 2. 3. 4. 5. 6. 7. 8. 9. 10. 11. 12. 13. 14. 15. 16. 17. 18. 19. 20. 21. 22. 23. 24. 25. typedef struct Friend } char name[MAX_LEN]; int numOfPresentsBoughtMe; } Friend; void sortFriendByPresents(Friend friends[], int size) } Friend temp; int i, j; { for (i=0 ; i < size ; i++) } for (j=i+1 ; j < size ; j++) } if (friends[i].numOfPresentsBoughtMe > friends[j].numOfPresentsBoughtMe) } temp = friends[i]; friends[i] = friends[j]; friends[j] = temp; { } { 34 © Keren Kalif )2( – דוגמא – החבר הכי טובfseek 1. 2. 3. 4. 5. 6. 7. 8. 9. 10. 11. 12. 13. 14. 15. 16. 17. 18. 19. 20. 21. 22. 23. void main() } FILE* f; Friend bestFriend; Friend myFriends[] = { {"momo", 5}, {"gogo", 2}, {"yoyo", 7} }; int numOfFriends = sizeof(myFriends) / sizeof(myFriends[0]); sortFriendByPresents(myFriends, numOfFriends); { f = fopen("myFriends.bin", "wb"); // check open file succeed... fwrite(&numOfFriends, sizeof(int), 1, f); fwrite(myFriends, sizeof(Friend), numOfFriends, f); fclose(f); numOfFriends = 0; // just reset to see correct value is read from file.. // get the friend that bought me most presents f = fopen("myFriends.bin", "rb"); // check open file succeed... fread(&numOfFriends, sizeof(int), 1, f); fseek(f, sizeof(Friend)*(numOfFriends-1), SEEK_CUR); //OR: fseek(f, sizeof(Friend)*-1, SEEK_END); fread(&bestFriend, sizeof(Friend), 1, f); fclose(f); printf("The friend who bought me most presents: %s\n", bestFriend.name); 35 © Keren Kalif ftell הפקודה מחזירה את מרחק הסמן מתחילת הקובץ long ftell (FILE * f); 1. #include <stdio.h> קריאת גודל של קובץ:דוגמא 2. 3. int main () 4. { 5. 6. FILE* f; long size; 7. 8. 9. f = fopen ("myFile.txt", "r"); // check if open succeed 10. 11. 12. 13. 14. 15. } fseek (f, 0, SEEK_END); size = ftell(f); fclose (f); printf ("Size of myfile.txt: %ld bytes.\n",size); 36 © Keren Kalif rewind הפקודה void rewind(FILE* f); 1. int main () 2. { 3. 4. 5. :מחזירה את הסמן לתחילת הקובץ ABC - קריאת וכתיבת כל ה:דוגמא int n; FILE *f; char buffer [27]; 6. 7. 8. f = fopen ("allLetters.txt","w+"); // check if open succeed 9. 10. 11. 12. 13. 14. 15. 16. 17. } for ( n='A' ; n<='Z' ; n++) fputc ( n, f); rewind (f); fscanf(f, "%s", buffer); fclose (f); buffer[26]='\0'; puts (buffer); 37 © Keren Kalif הפקודה fflush )int fflush(FILE* f מרוקנת את ה buffer -של הקובץ .בדיוק כמו שמנקים את ה- bufferמהמקלדת ע"י ) .fflsh(stdinמחזירה 0אם הצליחה. )(int fflush 38 מרוקנת את ה'buffer -ים של כל ה stream -הפתוחים (קבצים stdin ,וכד') .מחזירה 0אם הצליחה. © Keren Kalif ferror הפקודה במידה והפעולות שבוצעו על הקובץ תקינות0 פקודה זו מחזירה כאשר ננסה לכתוב לקובץ שנפתח לקריאה בלבד:דוגמא 1. 2. 3. 4. 5. 6. 7. 8. void main() { FILE* f = fopen("myfile.txt","r"); if (f == NULL) { printf("Error opening file\n"); return; } 9. fputc('x', f); if (ferror(f)) printf ("Error Writing to myfile.txt\n"); fclose(f); 10. 11. 12. 13. 14. } 39 © Keren Kalif דוגמא :ניהול קורסים וסטודנטים במכללה 40 דגשים לתוכנית: שימוש בקבועים פונקציות יעודיות להדפסה ולקבלת קלט מודולריות חלוקה לקבצים יצירת פונקציה עבור לוגיקה ספציפית ,עבור תחזוקה עתידית למשלgetDepartmentCodeFromCourseCode : © Keren Kalif קובץ אינדקסים -מוטיבציה 41 בהינתן קובץ טקסט עם שורות (ניתן להניח שאורך כל שורה הוא מקסימום 255תווים ,אבל לא בהכרח) נרצה לקרוא תו ספציפי בשורה מסוימת הפתרון :נקרא שורה-שורה עד השורה המבוקשת ,ובתוכה נזוז למיקום הרצוי דוגמא :בהינתן הקובץ הבא ,נרצה להגיע לתו ה 7 -בשורה 4 © Keren Kalif הקוד דילוג על השורות הראשונות. לא ניתן לדלג באמצעות fseek כי אורך השורות שונה. כאשר הסמן נמצא בתחילת השורה המבוקשת ,נקפוץ ישירות למיקום התו אותו רוצים לקרוא. 42 פעולת הקריאה מקובץ היא פעולה יקרה ולכן נרצה מנגנון אחר שיחסוך זאת. כלומר ,מנגנון שונה להגעה ישירה לשורה המבוקשת. )(1. void main { 2. 3. ;int line, col, i 4. ;char text[256], ch 5. ;)"FILE* f = fopen("test.txt", "r 6. 7. ;)" >printf("Enter line and col -- 8. ;)scanf("%d%d", &line, &col 9. 10. )for (i=1 ; i < line ; i++ 11. ;)fgets(text, 256, f 12. 13. ;)fseek(f, col-1, SEEK_CUR 14. ;)ch = fgetc(f 15. ;)printf("The char is '%c'\n", ch 16. 17. ;)fclose(f } 18. © Keren Kalif קובץ אינדקסים -בפתרון כאשר רוצים לבצע קריאות רבות ונקודתיות מקובץ בעל אורך שורות שונה שאינו משתנה ,נשתמש במנגנון הנקרא "קובץ אינדקסים" קובץ זה יכיל מספרים ,כאשר המספר ה i-הוא מיקום התו הראשון בשורה ה i -בקובץ דוגמא: שימושים :למשל עבור ספר טלפונים (נתוניו קבועים) ,נרצה לקבל נתונים של רשומה אחת כל שינוי בקובץ המקורי יגרור שינוי בקובץ האינדקס 43 © Keren Kalif יצירת קובץ אינדקס void main(int argc, char* argv[]) { char indexFileName[128], line[256]; FILE *textFile, *indexFile; int currentIndex, numOfChars; createIndexFileName(argv[1], indexFileName); textFile = fopen(argv[1], "r"); indexFile = fopen(indexFileName, "wb"); ,פתיחת קובץ הטקסט לקריאה וקובץ האינדקס בינארי לכתיבה fseek(textFile, 0, SEEK_END); numOfChars = ftell(textFile); נמצא את כמות התווים בקובץ הטקסט fseek(textFile, 0, SEEK_SET); currentIndex = 0; ,נמצא את מיקום הסמן בתחילת כל שורה while (currentIndex < numOfChars) ונכתוב ערך זה לקובץ האינדקסים { fwrite(&currentIndex, sizeof(int), 1, indexFile); fgets(line, 256, textFile); currentIndex = ftell(textFile); } fclose(textFile); fclose(indexFile); } קובץ האינדקס חייב להיות בינארי כדי שלםint שבקריאה נוכל לדלג כל פעם על 44 © Keren Kalif יצירת שם קובץ האינדקס :פונקציה זו יוצרת את שם קובץ האינדקס כך <file name>.txt עבור קובץ <file name>_index.bin :שם קובץ האינדקס יהיה test_index.bin שם ובץ האינדקס יהיהtest.txt עבור:דוגמא void createIndexFileName(const char* origName, char indexName[]) { strcpy(indexName, origName); strtok(indexName, "."); strcat(indexName, "_index.bin"); } 45 © Keren Kalif void main(int argc, char* argv[]) { FILE *textFile, *indexFile; int col, row, lineStartIndex; char ch; שימוש בקובץ אינדקס textFile = fopen(argv[1], "r"); indexFile = fopen(argv[2], "rb"); פתיחת קובץ הטקסט וקובץ האינדקס הבינארי לקריאה printf("Enter row and col --> "); scanf("%d%d", &row, &col); fseek(indexFile, (row-1)*sizeof(int), SEEK_SET); fread(&lineStartIndex, sizeof(int), 1, indexFile); fseek(textFile, lineStartIndex+col-1, SEEK_SET); fscanf(textFile, "%c", &ch); קריאה מקובץ האינדקס את מיקום תחילת השורה הזזת הסמן בקובץ הטקסט למיקום המבוקש וקריאה printf("The char is %c\n", ch); fclose(textFile); fclose(indexFile); } 46 © Keren Kalif דוגמאת סיכום :קבצים ,סיביותvariadic functions , כתוב פונקציה המקבלת שם של קובץ בינארי וכמות כלשהי של מספרים ,שנתון שסכומם הוא ( 8המספר האחרון יהיה ,0ליצוג סיום הפרמטרים) כל מספר מייצג כמות סיביות המייצגות מספר. יש לקרוא את הקובץ הבינארי ולהציג את המספרים המקודדים בתוכו. למשל ,עבור המספרים 4,1,3והבתים הבאים שנקראו מהזכרון (:)abc 01100001 01100010 01100011 יודפסו למסך המספרים הבאים: 47 © Keren Kalif 1. 2. 3. 4. #include <stdio.h> #include <stdlib.h> #include <string.h> #include <stdarg.h> 5. 6. 7. 8. unsigned char createMask(int high, int low); int getSubNumber(char ch, unsigned char mask, int high, int low); void readNumbers(const char* fileName, ...); 9. 10. 11. 12. 13. 14. 15. void main() { char str[] = "abc"; // a: 01100001 b: 01100010 c: 01100011 FILE* f = fopen("test.bin", "wb"); fwrite(str, sizeof(char), strlen(str), f); fclose(f); 16. readNumbers("test.bin", 4, 1, 3, 0); 17. 18. { 48 © Keren Kalif !מוזמנים לקרוא את ההסבר לפונקציה זו בדפים שבאתר /* This function receives higher and lower bounds of bit indexes, and creates a mask with 1 in this range, and 0 in all other places. I.E: high=5, low=3, the mask char would be: 00111000 */ 19. unsigned char createMask(int high, int low) 20. { 21. return ( 1 << ( high + 1 ) ) - ( 1 << low ); 22. { 64 8 1000000 1000 00111000 1 >> 6 1 >> 3 64-8=56 49 © Keren Kalif 28. 29. 30. 31. 32. 33. 34. 35. 36. 37. 38. 39. 40. 41. 42. 43. 44. 45. 46. 47. 48. 49. 50. void readNumbers(const char* fileName, ...) { FILE* f; va_list numbers; int highBit, lowBit, i, numOfNumbers=0, arr[8]; unsigned char ch; unsigned char* masks; f = fopen(fileName, "rb"); if (f == NULL) exit(1); // get the numbers va_start(numbers, fileName); arr[numOfNumbers] = va_arg(numbers, int); while (arr[numOfNumbers] != 0) } numOfNumbers++; arr[numOfNumbers] = va_arg(numbers, int); { // allocate the masks array masks = (char*)malloc(numOfNumbers*sizeof(unsigned char)); 50 © Keren Kalif 51. 52. 53. 54. 55. 56. 57. 58. 59. 60. 61. 62. 63. 64. 65. 66. 67. 68. 69. 70. 71. 72. 73. 74. 75. { // creates the masks highBit=7; for (i=0 ; i < numOfNumbers ; i++) } lowBit = highBit - arr[i] + 1; masks[i] = createMask(highBit, lowBit); highBit = lowBit - 1; } while (1) } fread(&ch, sizeof(char), 1, f); if (feof(f)) break; printf("From char '%c' --> ", ch); highBit=7; for (i=0 ; i < numOfNumbers ; i++) { lowBit = highBit - arr[i] + 1; printf("%d ", ((ch & masks[i]) >> lowBit)); highBit = lowBit - 1; } printf("\n"); { fclose(f); free(masks); 51 © Keren Kalif דוגמא :יצירת קובץ בינארי המכיל קידוד תמונה יש לייצר קובץ בינארי המכיל יצוג של תמונה. תמונה מורכבת מ N*M -פיקסלים ,כך שלכל פיקסל יש צבע (יש 16צבעים אפשריים). יש לכתוב לקובץ את מימדי התמונה ואח"כ את כל הפיקסלים לפי הסדר :הפיקסלים מהשורה הראשונה ,השניה וכו'. על מנת לשמור כל צבע ,ניתן להסתפק ב 4 -סיביות בכל byteניתן לשמור נתונים עבור 2פיקסלים 2בתים בהתחלה לשמירת מימדי התמונה 52 תמונה עם N*Mפיקסלים תשמר ב N*M/2 + 2 -בתים © Keren Kalif דוגמא :2שינוי צבע לפיקסל מסוים בתמונה 53 יש לתת קאורדינטת x,yוצבע ,ובגישה ישירה לעדכן את הפיקסל. יש לשים לב האם הצבע עבור הקאורדינטה המבוקשת נמצא בצד הימני אא בצד השמאלי של ה byte -בו הוא נשמר © Keren Kalif ביחידה זו למדנו: מוטיבציה אופרטורים לעבודה עם סיביות: 54 & | ^ ~ >> <<