ویرایش فایل در سی

ali0480

Member
سلام به همه دوستان
من دارم یه پروژه مدیریت کتاب های یه کتاب خونه رو می نویسم با استفاده از فایل ها و توی یه قسمتیش به یه مشکلی بر خوردم :
نیگا کاربر میادش اسم و مشخصات کتاب رو وارد می کنه و برنامه هر با که مشخصات وارد شدش مشخصات رو به فایلی به نام library.dat اضافه میکنه من تونستم این کارو بکنم اما برای حذف 1 کتاب که کاربر مشخص می کنه از فایل به مشکل بر خوردم
چه طور باید این کار رو بکنم ؟؟؟
مثلا نیگا کنید :
کاربر چند تا کتاب وارد کرد و این مشخصات توی فایلی روی هارد به اسم library.dat ذخیره شده مثلا اینا :

1.کتاب1 نویسنده1 قیمیت1
2.کتاب2 نویسنده2 قیمیت2
3.کتاب3 نویسنده3 قیمیت3
4.کتاب4 نویسنده4 قیمیت4
5.کتاب5 نویسنده5 قیمیت5

الان توی فایل ما مشخصات بالا ذخیره شده
حالااگه کاربر به خواد که کتاب 3 رو حذف باید چی کار کنم ؟؟؟؟چه طوری این کارو انجام بدم ؟؟؟میخوام واقعا حذف بشه ها نه اینکه نشون داده نشه موقع لیست کردن
خود فایل ها تابعی داره که این کار رو انجام بده یا نه ؟؟؟؟
 

the_king

مدیرکل انجمن
سلام به همه دوستان
من دارم یه پروژه مدیریت کتاب های یه کتاب خونه رو می نویسم با استفاده از فایل ها و توی یه قسمتیش به یه مشکلی بر خوردم :
نیگا کاربر میادش اسم و مشخصات کتاب رو وارد می کنه و برنامه هر با که مشخصات وارد شدش مشخصات رو به فایلی به نام library.dat اضافه میکنه من تونستم این کارو بکنم اما برای حذف 1 کتاب که کاربر مشخص می کنه از فایل به مشکل بر خوردم
چه طور باید این کار رو بکنم ؟؟؟
مثلا نیگا کنید :
کاربر چند تا کتاب وارد کرد و این مشخصات توی فایلی روی هارد به اسم library.dat ذخیره شده مثلا اینا :

1.کتاب1 نویسنده1 قیمیت1
2.کتاب2 نویسنده2 قیمیت2
3.کتاب3 نویسنده3 قیمیت3
4.کتاب4 نویسنده4 قیمیت4
5.کتاب5 نویسنده5 قیمیت5

الان توی فایل ما مشخصات بالا ذخیره شده
حالااگه کاربر به خواد که کتاب 3 رو حذف باید چی کار کنم ؟؟؟؟چه طوری این کارو انجام بدم ؟؟؟میخوام واقعا حذف بشه ها نه اینکه نشون داده نشه موقع لیست کردن
خود فایل ها تابعی داره که این کار رو انجام بده یا نه ؟؟؟؟

اگه ترتیب قرار گیری رکورد ها اهمیت داره و یا موارد حذفی بیشتر از یک مورده و یا طول رکورد ها متغیره
و همه آنها یک طول ثابت ندارند :
  • یک فایل جدید موقتی مثلا به نام temp.dat ایجاد کنید.
  • رکورد ها را از ابتدا یکی یکی از داخل library.dat بخوانید.
  • اگر آن رکورد با مشخصات حذفی مورد نظرتان مطابقت نداشت در فایل temp.dat درج اش کنید.
  • خواندن رکورد ها را ادامه دهید تا به پایان فایل library.dat برسید.
  • فایل library.dat را حذف کرده و نام temp.dat را به library.dat تغییر دهید.
این روش زمان زیادی را صرف می کند، هر چه فایل طولانی تر باشد، کندتر خواهد شد. به فضای اضافی دو برابری هم
نیاز دارد. در مقابل می تواند چندین رکورد را یکجا حذف کند و ترتیب قرار گیری رکورد ها به هم نمی ریزد.

اگه هم ترتیب قرار گیری رکورد ها اهمیتی ندارد و هم مورد حذفی تنها یک رکورده و هم طول رکورد ها ثابت است :
  • فایل library.dat را باز کنید و آخرین رکورد را از انتهای فایل بخوانید، چون طول رکورد ها ثابت است و طول
    فایل هم مشخص است، شناسایی موقعیت دقیق آخرین رکورد نیز مشخص خواهد بود.
  • رکورد ها را از ابتدای فایل یکی یکی بخوانید تا به موقعیت رکوردی برسید که باید حذف شود.
  • آخرین رکورد فایل که قبلا خوانده اید را روی رکورد حذفی بازنویسی کنید.
  • در انتهای فایل رکورد آخر را با یک رکورد خالی بازنویسی کنید و یا طول فایل را به اندازه یک رکورد کاهش دهید.
این روش سریعتر است و نیازی به فضای اضافه ندارد، اما ترتیب قرار گیری رکورد ها را تغییر می دهد و تنها برای
حذف کردن یک رکورد مناسب است. برای حذف کردن چندین رکورد بایستی مراحل آنرا با کمی خلاصه سازی مجددا تکرار کنید.
 

ali0480

Member
سلام the king جان
اول اینو بگم فکنم مهم باشه ما توی فایلمون می خواییم تعداد نامعلومی (هر چند تا که کاربر خواستش) کتاب توی فایل ذخیره کنیم که همشونم از یه نوع strcut هستن یعنی :

کد:
struct library {
char bookname[10] , writer [10] ;
int cost ;
}book;
یعنی ما اول struct بالا رو به صورت global تعریف می کنیم و بعدش هر بار که خواستیم کتابی رو به فایل اضافه کنیم اول داخل book مشخصات رو ثبت می کنیم بعدش متغییر book رو که از جنس struct library هستش رو به فایل اضافه می کنیم این روند به هر تعدادی که کماربر بخواد انجام میشه .گفتم اینو بگم شاید بهتر باشه .خوب حالا راه های گه کفته بودی :
راه دومی که گفتی جالب بودش و به چندتا نکته ای هم تو نوشتت اشاره کردی که به نظرم مهم اومدش و جواب یه سری سوالام بودش .
نیگا روش اولی که گفتی و بلد بودم اما چون می خواستم ذهنیتی ایجاد نشه و فقط 1 راه گفته نشه ذکرش نکردم چون استادمون هم روش اول رو فقط گفتش ، اما یه مشکلی هستش من این کار رو انجام دادم مثلا اگه فرض کنیم ما توی فایلمون 5 تا struct ذخیره کرده باشیم به این شکل :

کد:
book1 writer1 1000
book2 writer2 2000
book3 writer3 3000
book4 writer4 4000
book5 writer5 5000
حالا اگه من بخوام book2 رو حذف کنم باید کل struct ی که شامل Book2 هستش رو حذف کنم منم اومدم گفتم فایل رو بگرد و تا هر موقع که به structی که اسم کتابش book2 نیستش برخورد کردی بریزش توی یه فایل جدید و اگه به یه struct با این اسم هم بر خورد کردی هیچ کاری نکن و ردش کن برو بعدی این کار رو توی یه حلقه while به این شکل انجام دادم :

کد:
while (!feof(f1))
{
statment;
}
که f1 یک متغییر از نوی pointer هستش که به فایل مورد نظر اشاره می کنه و خلاسه میادش کل struct ی که نام مورد نظرش این هستش رو پاک می کنم اما فقط 1 اشکال داره اونم اینه که strcut بعدی رو جاش کپی میکنه و موقع نمایش ما از struct بعدی اون 2 تا داریم به این شکل (اگه بخواییم book2 حذف بشه ) :

قبل از حذف :


کد:
book1 writer1 1000
book2 writer2 2000
book3 writer3 3000
book4 writer4 4000
book5 writer5 5000
بعد از حذف :


کد:
book1 writer1 1000
book3 writer3 3000
book3 writer3 3000
book4 writer4 4000
book5 writer5 5000
می بینی از struct بعدی Book2 یعنی book3 2تا داریم یعنی Book2 pbt ani اما به جای این که جاش خالی بشه struct بعدی اون یعنی book3 جاش کپی شده و از book3 دو تا داریم . حالا سوال من اینه که چرا ؟؟؟چرا این طوری میشه ؟و چطور می شه این مشکل رو حل کردش ؟؟؟ که توی اخر راه حل دومت گفتی باید یه دونه از تعداد struct های توی فایلم کم کنم این به این مشکل من فکنم ربط داشته باشه نه ؟؟؟به خاطر همین به نظرم جالب اومدش اما چه طور 1 دونه از تعداد struct ]ای فایلم کم کنم ؟؟؟
بازم سوال فنی دارم اما فکنم این پست زیاد شدش دفعه بعدی ازت می پرسم خیلی خیلی مرسی:rose:
 

the_king

مدیرکل انجمن
مشکل شما در مورد اون رکورد تکراری در پیاده سازی اشتباهه، وگرنه الگوریتم مشخصه. باید کدتون رو بررسی کنید.

برای تغییر حجم یک فایل توابعی وجود داره که حجم فایل رو بطور دقیق مشخص می کنند :

در کامپایلر های بورلند تابع ()chsize بکار میره :
کد:
#include <io.h>
int chsize(int handle, long size);

مثلا حجم فایلی که با متغیر f باز شده را به 1000 بایت تغییر می دهیم :
کد:
chsize(f, 1000);

در کامپایلر های مایکروسافت تابع ()chsize_ بکار میره :
کد:
#include <io.h>
int _chsize( int handle, long size );

در کامپایلر های تحت لینوکس و یونیکس از ()ftruncate استفاده میشه :
کد:
#include <unistd.h>
int ftruncate (int fd, off_t length);
 

ali0480

Member
دوباره سلام ولی فکنم الگریتمم درست باشه ایناهاشش نیگا این تابع حذف کتاب از فایل کتاب خونه هستش :

کد:
void remove (void)
{
flib=fopen("library.dat","a+b");
fnewlib=fopen("newlibrary.dat","wb");
 if((!fnewlib)||(!flib))
    {
       printf("Error : the file can't be opend.");
       exit(1);
    }
rewind(flib);
char delname[10];
int sign=0;
 printf("\nEnter name of the book that you want to remove it :");
 scanf("%s",delname);
     fread(&book,sizeof(struct library),1,flib);
       if(strcmp(delname,book.name)!=0)
           fwrite(&book,sizeof(struct library),1,fnewlib);
       else
       sign=1;
     while(!feof(flib))
     {
       fread(&book,sizeof(struct library),1,flib);
       if(strcmp(delname,book.name)!=0)
           fwrite(&book,sizeof(struct library),1,fnewlib);
       if(strcmp(delname,book.name)==0)
       sign=1;
     }
    fclose(flib);fclose(fnewlib);
    remove("library.dat");
    rename("newlibrary.dat","library.dat");   
 if(sign==0)
   printf("\n\t\athere isn't any book with this name.\n");
 else
   printf("\n\tthe book remove from library succesfuly.\n");
}

اون وقت این تغییر حججمی که توضیح دادید برای چیه ؟؟؟؟ من که همچین سوالی نداشتم ؟؟؟
 

the_king

مدیرکل انجمن
اون وقت این تغییر حججمی که توضیح دادید برای چیه ؟؟؟؟ من که همچین سوالی نداشتم ؟؟؟

توی اخر راه حل دومت گفتی باید یه دونه از تعداد struct های توی فایلم کم کنم این به این مشکل من فکنم ربط داشته باشه نه ؟؟؟به خاطر همین به نظرم جالب اومدش اما چه طور 1 دونه از تعداد struct ]ای فایلم کم کنم ؟؟؟
برای حذف کردن یک رکورد از انتهای فایل، طول فایل را به اندازه یک رکورد کاهش خواهید داد.

کد:
void remove (void)
{
	flib=fopen("library.dat","a+b");
	fnewlib=fopen("newlibrary.dat","wb");
	if ((!fnewlib)||(!flib))
	{
		printf("Error : the file can't be opend.");
		exit(1);
		}
	rewind(flib);
	char delname[10];
	int sign=0;
	printf("\nEnter name of the book that you want to remove it :");
	scanf("%s",delname);
	while(!feof(flib))
	{
		fread(&book,sizeof(struct library),1,flib);
		if (strcmp(delname,book.name)!=0)
			fwrite(&book,sizeof(struct library),1,fnewlib);
		else
			sign=1;
	}
	fclose(flib);fclose(fnewlib);
	remove("library.dat");
	rename("newlibrary.dat","library.dat");
	if(sign==0)
		printf("\n\t\athere isn't any book with this name.\n");
	else
		printf("\n\tthe book remove from library succesfuly.\n");
}
 

ali0480

Member
این کدی که ویرایش کردی the king فرق زیادی با کد اولیم نداشتا ؟؟؟
ولی فکنم مشکلم حل شده باشه برای این می گم فکنم چون نیگا من یه انتخاب برای کاربر گذاشتم که هر وقت خواستش توسط تابع show لیست کتابای موجود رو که توی فایل هستن براش روی صفحه چاپ می کنه اما همیشه item اخر رو 2 باز چاپ می کنه اگه 2 تا کتاب داشته باشیم دومین کتاب رو دوبار چاپ مکنه اگه 5 تا کتاب داشته باشم پنجمین کتاب رو 2 بار چاپ می کنه و .... .
اینم از کد تابع show :
کد:
void show (void)
{
 flib=fopen("library.dat","rb");
 puts("There is these book in the library :\n");
 if(!flib)
     {
       printf("Error : the file cann't be opened.");
       exit(1);
     }         
     while(!feof(flib))
         {
             fread(&book,sizeof(struct library),1,flib);
             printf("book's name : ");
             printf("%s",book.name);
             printf("    writer's name : ");
             printf("%s",book.writer);
             printf("    coust : ");
             printf("%d",book.coust);
             printf("\n");     
        }
 fclose(flib);    
}

به نظرت مشکلش کجاس ؟؟ البته می دونما مشکلش از شرط حلقه هستش همین اگه قرار باشه حلقه n بار بچرخه n+1 بار می چرخه نمی دونم چرا هیچ جایی هم ننوشته چرا !!!!! میشه تابع feof() رو توضیح بدی چه طور کار می کنه و هر دفعه به کجای فایل اشاره مکنه ؟؟
 

ali0480

Member
این کدی رو هم که دادی امتحان کردم قبل حذف وقتی که 4 تا Item اضافه کردم خروجی این بودش :
کد:
book1 writer1 1000
book2 writer2 2000
book3 writer3 3000
book4 writer4 4000
book4 writer4 4000

اما وقتی book2 رو حذف کردم این طوری شدش :
کد:
book1 writer1 1000
book3 writer3 3000
book4 writer4 4000
book4 writer4 4000
book4 writer4 4000


چرا ؟؟؟
 

ali0480

Member
می دونم شاید الان بگید همه حرفامو توی 1 پست بزنم شرمنده سعی میکنم از دفعه بعد رعایت کنم.
من کد خودم و کدی که شما ویرایش کردید رو همومن طوری که فکر میکردم فرقی نداره امتحان کردم و حدسم درست بودش فرقی نداشت شاید من دفعه اول توی گفتنم بد گفتم چون الان همون طوری که گفتم book4 رو 3 بار نشوم دادش و وقتی هم که بعد از حذف book2 وقتی Book5 رو اضافه کردمعلاوه بر 2بار نشون دادن اخرین item یعنی book5 کتاب book4 رو چون book2 حذف شده بود رو هم 2 بار نشون دادش ؟؟؟
اصلا بزار یکار بهتر نیگا این چند تا از تابع های برنامه من هستش تابع :
1-تابع add : اضافه کردن کتاب
2-تابع remove : حذف کتاب
3-تابع show: نشان دادن کتاب های موجود در فایل .

-تابع add :
کد:
void add (void)
{
flib=fopen("library.dat","ab");
if((!flib))
 {
   printf("Error : the program can't open the file.");
   exit(1);
 }
 printf("\n\nEnter name of the new book : ");
 scanf("%s",&book.name);
 printf("\nEnter the name of writer :");
 scanf("%s",&book.writer);
 printf("\nEnter the cust of the book :");
 scanf("%d",&book.coust);
 printf("\n");
 printf("\n\tthe book add successfuly");
 fwrite(&book,sizeof(struct library),1,flib);
 fclose(flib);
}

تابع show و remove هم که بالا گذاشتم .
راستی برای نوشتن روی یک رکوردی که قبلا ثبت شده ببین این الگریتمی که می گم درسته :
1- مشخصات کتاب جدید رو میگیریم.
2-دونه به دونه از اول فایل رکورد ها رو می خونیم و توی یه struct مناسب توی برنامه میریزیم
3-اگه رکورد خونده شده و ثبت شده از فایل به داخل برنامه رکورد مورد نظر بودش با تابع strcpy() مشخصاتی رو که قبلا گرفتیم داخل رکورد درون برنامه میریزیم
4-رکورد داخل برنامه رو داخل فایل با fread() مثلا چاپ می کنیم
اره ؟؟؟
حالا 1 سوال ایا این میره روی همون رکوردی که آخرین باز خونده شده رکورد جدید رو ثبت می کنه یا کنار اون ؟؟؟

بازم شرمنده به خاطر چند پستی شدن سوالای آخرم
 

the_king

مدیرکل انجمن
این کدی رو هم که دادی امتحان کردم قبل حذف وقتی که 4 تا Item اضافه کردم خروجی این بودش :
کد:
book1 writer1 1000
book2 writer2 2000
book3 writer3 3000
book4 writer4 4000
book4 writer4 4000

اما وقتی book2 رو حذف کردم این طوری شدش :
کد:
book1 writer1 1000
book3 writer3 3000
book4 writer4 4000
book4 writer4 4000
book4 writer4 4000


چرا ؟؟؟

ایراد مربوط به نحوه عملکرد ()feof است. ()feof زمانی مقدار مخالف صفر بر می گرداند که قبل از آن یک تلاش
ناموفق برای خواندن اطلاعات فراتر از طول فایل رخ داده باشد. زمانی که شما آخرین رکورد فایل را می خوانید، هنوز یک
تلاش ناموفق رخ نداده است. به همین جهت آخرین رکورد مجددا در حلقه while تکرار می شود. این جزو معایب روتین های
زبان C است. به عنوان مثال فایل test.txt را که فقط چهار کاراکتر ABCD در آن ذخیره شده باشد (طول فایل 4 بایت
باشد) را با کد زیر امتحان کنید :
کد:
#include <stdio.h>

int main()
{
	FILE *f = fopen("test.txt", "rb");
	char ch;
	if(f)
	{
		while(!feof(f))
		{
			fread(&ch, 1, 1, f);
			printf("char = %c \n", ch);
		}
		fclose(f);
	}
	else
	{
		printf("Error : the test.txt not found!");
		return 0;
	}
}
به شما خروجی زیر را نشان خواهد داد :
کد:
char = A
char = B
char = C
char = D
char = D

برای رفع این مشکل بجای ()feof از تابع ()feof2 زیر استفاده کنید :
کد:
int [B]feof2[/B](FILE *stream)
{
	long int pos = ftell(stream);
	fseek(stream, 0, SEEK_END);
	long int last = ftell(stream);
	fseek(stream, pos, SEEK_SET);
	return (pos < last) ? 0 : -1;
}
 

the_king

مدیرکل انجمن
حالا 1 سوال ایا این میره روی همون رکوردی که آخرین باز خونده شده رکورد جدید رو ثبت می کنه یا کنار اون ؟؟؟

در کنار اون، در واقع روی رکورد بعدی بازنویسی میشه که ربطی به رکورد مورد نظر نداشته.
برای آنکه درست روی رکوردی که لحظه پیش خوانده بودید بازنویسی بشه، بایستی با تابع ()fseek موقعیت
اشاره گر را به اندازه یک رکورد عقب بکشید :

کد:
fseek(flib,-sizeof(struct library),SEEK_CUR);
 

ali0480

Member
که این طور ، خیلی خیلی ممنون
فقط چند تا سوال دیگه :
1- تابع بثخب)( اگه به انتهای فایل رسیده باشیم غیر صفر و اگه نرسیده باشم صفر بر می گردونه درسته ؟؟؟ حالا اونوقت این عدد غیر صفر چند هستش ؟؟ -1 یا 1 یا یه عدد رندمی ؟؟؟
2- تابع foef2() رو خودم باید تعریف کنم یا تعریف شده C هستش ؟؟؟
3- میشه یه نمور در مورد دستورات این تابع foef2() یی که نوشتی توضیح بدی ؟؟؟
 

جدیدترین ارسال ها

بالا