יום ראשון, 5 בפברואר 2017

שפת C - קבצים בינארים וקריאה לא סדרתית

קבצים בינארים


כפי שלמדנו בשיעור הקודם, כדי לכתוב/לקרוא מקובץ צריך לפתוח אותו ובסוף גם לסגור אותו:
void main( ) {
FILE fp ;
fp = fopen ("name_of_file","mode") ;
.
.
.
fclose(fp);
}
למדנו בקצרה שעבור קבצים בינארים יש ב mode את האות b.








פעולות בסיסיות על קובץ בינארי:



fwrite( מצביע קובץ , מספר המשתנים , גודל המשתנה בבתים , כתובת המשתנה ממנו מעתיקים )
fwrite( void *p , int size , int num , FILE *fp )                          :ניסוח באנגלית
קולטת מהמשתנה עליו מצביע p , על פי הגודל של size (שהוא הסוג של המשתנה p), כפול num פעמים. (num יהיה 1
עבור משתנה יחיד, או יותר עבור כל איבר שרוצים להעתיק (אם p הוא מערך)) ו"מדפיסה" את כל זה באופן בינארי לקובץ
עליו מצביע fp .



דוגמה:
#include <stdio.h>
#include <stdlib.h>
void main() {
 FILE *fp;
 int A[10] = {10,20,30,40,50,60,70,80,90,100};
 double t  = 3526.3025;
 char str[10] = "Hello";
 fp = fopen("t.bin" , "wb"); // יצור קובץ בשם זה בתיקיית התוכנית
 if(fp == NULL) {
  printf("Can't open file\n");
  exit(0);
 }
 fwrite(A, sizeof(int) ,10,fp);  // להזכירכם שם של מערך הוא מצביע לתחילתו
 fwrite(&t, sizeof(double) ,1,fp);
 fwrite(str, 1 ,10,fp); // "sizeof(char)" :במקום "1" אפשר גם היה לכתוב  
 
 fclose(fp);
}







fread( מצביע קובץ , מספר המשתנים , גודל המשתנה בבתים , כתובת המשתנה אליו מעתיקים )
fwrite( void *p , int size , int num , FILE *fp )                          :ניסוח באנגלית
מעתיקה למשתנה עליו מצביע p , ערכים בינארים מהקובץ עליו מצביע fp .
על פי הגודל של size
 (שהוא הסוג של המשתנה p), כפול num פעמים.
(num יהיה 1 עבור משתנה יחיד, או יותר עבור כל איבר שרוצים להעתיק (אם p הוא מערך)).


דוגמה: (הערה: משום שקובץ בינארי הוא לא קריא לנו, נשתמש כאן באותו קובץ שיצרנו בדוגמה הקודמת)

#include <stdio.h>
#include <stdlib.h>
void main() {
 FILE *fp;
 int B[10] , i;
 double t;
 char str[10];
 fp = fopen("t.bin" , "rb");
 if(fp == NULL) {
  printf("Can't open file\n");
  exit(0);
 }
 fread(B, sizeof(int) ,10,fp);
 fread(&t, sizeof(double) ,1,fp);
 fread(str, 1 ,10,fp);
 
 for(i=0; i<10; i++)
  printf("%d ",B[i]);
 
 printf("t = %f    str: %s\n",t , str);
 
 fclose(fp);
 
}





קריאה, וקריאה לא סדרתית

כפי שלמדנו, כל FILE מוגדר למעשה כ struct בו מלבד השדה של התוכן העיקרי של הקובץ (לדוגמה השדה בו נמצאים כל התווים בקובץ טקסט) קיימים עוד הרבה שדות. אחד מהם נקרא "מצביע הקריאה" ומטרתו היא להגיד לפונקציות מאיפה להתחיל לקרוא/לכתוב לקובץ. את מצביע הקריאה אנחנו לא צריכים לקדם כל פעם משום שכל פונקציה שעושה בו שימוש מקדמת אותו לסוף הקריאה/הכתיבה שהיא ביצעה.

בכל הדוגמאות שעשינו עד עכשיו, מצביע הקריאה התקדם מאחורי הקלעים.





קריאה לא סדרתית
נוכל להזיז באופן ידני את מצביע הקריאה של הקובץ, מה ששימושי לעיתים.


fseek( מאיפה מתחילים להזיז , כמות הבתים שזזים , מצביע קובץ )                              
fseek( FILE *fp , int offset , int whence )                          :ניסוח באנגלית
בקובץ עליו מצביע fp, מזיזה את מצביע הקריאה offset בתים (offset הוא מספר חיובי- קדימה. שלילי- אחורה.).
מתחילה להזיז מ whence.

whence
יכול לקבל אחד משלושה דברים:
(אלו למעשה קבועים השווים בערכם למספרים. לכן whence הוא int בהגדרת הפונקציה למעלה)


 SEEK_SET    -מתחילת הקובץ  (ה offset תמיד יהיה חיובי)
SEEK_CUR    -מהמקום הנוכחי  (ה offset יהיה חיובי או שלילי)
SEEK_END    -מסוף הקובץ      (ה offset תמיד יהיה שלילי)






ftell( מצביע קובץ )                                   
ftell( FILE *fp)               :ניסוח באנגלית
מחזירה את המיקום הנוכחי של מצביע הקריאה. בלי לשנות אותו.




* כך נוכל לדעת את כמות הבתים בקובץ:
fseek (fp,0,SEEK_END) ;                  
x = ftell (fp) ;                                     









דוגמאות:

#include <stdio.h>
#include <stdlib.h>
void main() {
 FILE *fp;
 char ch;
 fp = fopen("tt.txt" , "r"); // בתיקיית התוכנית tt.txt יש ליצור קובץ טקסט בשם
 if(fp == NULL) {
  printf("Can't open file\n");
  exit(0);
 }
 putchar( fgetc(fp) );
 fseek(fp, 1 , SEEK_CUR);
 putchar( fgetc(fp) );
 fseek(fp, -1 , SEEK_END);
 putchar( fgetc(fp) );
 printf("\n%d\n",ftell(fp) );
 putchar( '\n');
 fclose(fp);
}





משימה:
כתוב תוכנית המכילה מבנה המייצג מוצר מסוים.
נתוני המבנה הם:
- שם המוצר.
- המחיר שלו.
- הכמות שנמצאת בחנות.
יש לבנות קובץ בשם "prod.txt" המכיל נתונים של מספר מוצרים.
צרו תוכנית שקולטת אותם מהקובץ ומוצאת את:

א. כמה מוצרים בחנות. (כלומר כמה שמות של מוצרים יש בחנות. לא כמה פריטים. 5אבוקדו ו8 גזר  =2 מוצרים)
ב. עלות כל המוצרים שנמצאים בחנות.
ג. שם המוצר ששווה הכי הרבה.

בצעו את המשימה פעם אחת עבור קובץ טקסט ופעם אחת עבור קובץ בינארי!
(כדי ליצור את הקובץ הבינארי תצטרכו תוכנית נוספת שיוצרת קובץ בינארי על פי קובץ הטקסט)












פתרון: (נסו קודם לבד)

תוכנית ראשונה: (מקובץ טקסט)

#include <stdio.h>
#include <stdlib.h>
struct prod {
 char name[10] ;
 double price;
 int cnt;
};
void main() {
 FILE * fp;
 struct prod  p , Pmax={0};
 double sum = 0;
 int c=0;
 
 fp = fopen("prod.txt" , "r");
 if(fp == NULL) {
  printf("Can't open file\n");
  exit(0);
 }
 while(! feof(fp) ) {
  fscanf(fp, "%s %lf %d",p.name, &p.price, &p.cnt);
  if( feof(fp) )break// שורה זו משום שמצביע הקריאה מראה "סוף התוכנית" רק בקליטה/קריאה אחת נוספת אחרי התו האחרון
 
  printf("%s\n",p.name);
  c++;
  sum += p.cnt * p.price; // מחשב עלות כל המוצרים בחנות יחד
  if(p.price > Pmax.price)
   Pmax = p;
 }
 printf("c: %d    Sum: %.2f   Name: %s\n",c, sum , Pmax.name);
 fclose(fp);
}


תוכנית שנייה: (להעתיק מקובץ טקסט לקובץ בינארי)

#include <stdio.h>
#include <stdlib.h>
struct prod {
 char name[10] ;
 double price;
 int cnt;
};
void main() {
 FILE *fp1, *fp2;
 struct prod  p , Pmax={0};
 double sum = 0;
 int c=0;
 
 fp1 = fopen("prod.txt" , "r");
 fp2 = fopen("prod.bin" , "wb");
 if(fp1 == NULL || fp2 == NULL) {
  printf("Can't open file\n");
  exit(0);
 }
 while(! feof(fp1) ) {
  fscanf(fp1, "%s %lf %d",p.name, &p.price, &p.cnt);
  if( feof(fp1) )break;
  fwrite(&p, sizeof(p), 1, fp2);
 }
 fclose(fp1);
 fclose(fp2);
}



תוכנית שלישית: (מקובץ בינארי)

#include <stdio.h>
#include <stdlib.h>
struct prod {
 char name[10] ;
 double price;
 int cnt;
};
void main() {
 FILE * fp;
 struct prod  p , Pmax={0};
 double sum = 0;
 int c=0;
 
 fp = fopen("prod.bin" , "rb");
 if(fp == NULL) {
  printf("Can't open file\n");
  exit(0);
 }
 while(! feof(fp) ) {
  fread(&p, sizeof(p), 1, fp);
  if( feof(fp) )break;
 
  printf("%s\n",p.name);
  c++;
  sum += p.cnt * p.price;
  if(p.price > Pmax.price)
   Pmax = p;
 }
 printf("c: %d    Sum: %.2f   Name: %s\n",c, sum , Pmax.name);
 fclose(fp);
}











עד כאן על קבצים.



אין תגובות:

הוסף רשומת תגובה