ফাইল ইনপুট আউটপুট সিস্টেম (File I/O)

FILE I/O (ফাইল ইনপুট / আউটপুট)

আমরা ইতোমধ্যে scanf এবং printf ফাংশন গুলো ডেটা ইনপুট এবং আউটপুট দিতে ব্যবহার করেছি। এরা console oriented I/O ফাংশন, অর্থাৎ এরা টার্মিনাল (কিবোর্ড/স্ক্রীন) এর উপর নির্ভর করে ইনপুট / আউটপুট দিয়ে থাকে। সমস্যা হল-
বড় আকারের ডেটা নিয়ে কাজ করার সময় দুই ধরনের বাধার সম্মুখীন হতে হয়।

১. বড় আকারের ডেটা টার্মিনালে ইনপুট দিতে অনেক সময় লাগে। আউটপুট এর ক্ষেত্রেও আউটপুট ম্যানেজ করা অসুবিধাজনক।
২. প্রোগ্রাম শেষ হলে বা কম্পিউটার বন্ধ করলে পুরো ডেটাই হারিয়ে যাবার সম্ভাবনা থাকে।

এই জন্য টার্মিনাল ছাড়াও আরো সুবিধাজনক ইনপুট আউটপুট সিস্টেম ব্যবহার করার দরকার পরে।

FILE এবং STREAM

C ল্যাংগুয়েজ এ ফাইল বলতে অনেক ধরনের I/O ডিভাইসকে বুঝানো হয়। তবে সবচে বেশি ব্যবহৃত হয় disc file কে বুঝানোর জন্য।

প্রোগ্রামারের সুবিধার জন্য C, প্রোগ্রামার এবং বিভিন্ন ধরনের ফাইলের মধ্যে সম্পর্ক স্থাপনের জন্য স্ট্রিম নামে একধরনের এবস্ট্রাক্ট পদ্ধতি ব্যবহার করে। এর সুবিধা হল, প্রোগ্রামার একই ধরনের I/O ফাংশন বিভিন্ন ধরনের ফাইলের সাথে ব্যবহার করতে পারেন। ফাইলের বিভিন্নতা অনুধাবন করে যথাযথ স্ট্রীম ডেটাকে ইনপুট ও আউটপুট দিয়ে থাকে, যদিও প্রোগ্রামার একই ধরনের ফাংশন ব্যবহার করেন। ফলে ফাইল টাইপ অনুযায়ী প্রোগ্রামারকে ভিন্ন ভিন্ন প্রোগ্রাম লিখতে হয় না। অর্থাৎ,

প্রোগ্রামার যখন I/O ফাংশন ব্যবহার করেন, তখন স্ট্রীম এর কাজ হল নির্ধারিত ফাইলের সাথে সেই ফাংশনের সম্পর্ক স্থাপন করা এবং ডেটা ব্যবস্থাপনা করা।

একটি open operation এর মাধ্যমে একটি স্ট্রীমের সাথে একটি ফাইলের সম্পর্ক স্থাপন করা হয়।

একটি close operation এর মাধ্যমে একটি স্ট্রীমের সাথে একটি ফাইলের পূর্বে স্থাপিত সম্পর্ক বিচ্ছিন্ন করা হয়।

স্ট্রীম দুই ধরনের।
১. টেক্সট স্ট্রীম
২. বাইনারি স্ট্রীম

টেক্সট স্ট্রীম ASCII ক্যারেক্টার সেট ব্যবহার করে ইনপুট আউটপুট দিয়ে থাকে এবং তা ব্যবহারকারী পড়তে পারে। অর্থাৎ টেক্সট স্ট্রিম ফাইল হতে পড়ার সময় বা ফাইলে লেখার সময় যথাক্রমে human readable থেকে machine readable ভাষায় ইনপুট কে এবং machine readable থেকে human readable ভাষায় আউটপুট কে অনুবাদ করে থাকে। এটা সুবিধাজনক মনে হলেও, তা সবক্ষেত্রে নয়। যেমন, এই অনুবাদের কারনে \n ক্যারেকটার টি 'carriage return+line feed pair' এ রূপান্তরিত হয়। এই পরিবর্তন এর কারনে আসল ডেটা ও ফাইলে লেখা ডেটার মধ্যে এক-এক মিল করন বেশিরভাগ ক্ষেত্রেই সম্ভব হয় না।

বাইনারি স্ট্রীম বাইনারি হিসেবে ডেটা ব্যবস্থাপনা করে। এটা ব্যবহারকারীর অনুধাবন যোগ্য নয়, তবে এতে ডেটায় কোন ধরনের পরিবর্তন হয় না। কাজেই আসল ডেটাই ফাইলে জমা থাকে।

স্ট্যান্ডার্ড তিনটি স্ট্রীম হল stdin, stdout এবং stderr যারা যথাক্রমে কিবোর্ড, স্ক্রীন এবং স্ক্রীন এর সাথে ইনপুট ও আউটপুট ফাংশনের সম্পর্ক স্থাপন করে থাকে।

সংক্ষেপে-
* ফাইল হল I/O ডিভাইস।
* স্ট্রীম হল I/O ফাংশন ও I/O ডিভাইসের মধ্যে সম্পর্ক স্থাপনকারী।
** পরবর্তী আলোচনায়, ফাইল বলতে আমরা disc file কেই বুঝাবো।

FILE OPERATIONS

ফাইল নিয়ে কাজ করাকে বলে ফাইল অপারেশন। মৌলিক ফাইল অপারেশন এর মধ্যে রয়েছে,

* ফাইল খোলা ( নতুন ফাইল তৈরী, ফাইলের নামকরণ ও অবস্থান নির্ধারণ, কি কাজে ফাইল খোলা হবে তার ডিক্লারেশন ইত্যাদি)

* ফাইল হতে ডেটা read করা

* ফাইলে ডেটা write করা

* ফাইল বন্ধ করা

এছাড়াও কিছু উচ্চতর ফাইল অপারেশন রয়েছে। নিচের ছকে ফাইল অপারেশন এর সংক্ষিপ্ত বর্ণনা দেওয়া হল:

[এই ছকের কিছু না বুঝলে ভয়ের কারন নাই। পরের বিস্তারিত আলোচনায় আশা করি সব পরিস্কার হয়ে যাবে।]

fopen()
* নতুন ফাইল তৈরী
* ফাইলের নামকরণ
* ফাইলের অবস্থান নির্ধারণ
* পূর্বের তৈরী করা আছে এমন কোন ফাইল খোলা
* ফাইল খোলার পরে কি করা হবে তা প্রকাশ করা

fclose()
* ব্যবহারের জন্য খোলা হয়েছিল এমন ফাইল বন্ধ করা

getc()/fgetc()
* ফাইল থেকে একটি করে ক্যারেক্টার read করে

putc()/fputc()
* ফাইলে একটি করে ক্যারেক্টার write করে

getw()
* ফাইল হতে একটি ইন্টিজার read করে

putw()
* ফাইলে একটি ইন্টিজার write করে

fgets()
* ফাইল হতে স্ট্রিং read করে

fputs()
* ফাইলে স্ট্রিং write করে

fread()
* ফাইল হতে বাইনারি ডেটা read করে

fwrite()
* ফাইল এ বাইনারি ডেটা write করে

fprintf()
* ফাইলে এক সেট ডেটা write করে

fscanf()
* ফাইল হতে এক সেট ডেটা read করে

fseek()
* ফাইলের একটি নির্ধারিত অংশে যাওয়ার জন্য ব্যবহৃত হয়

ftell()
* ফাইলের যে অবস্থানে বর্তমানে ফাইল অপারেশন চলছে (current position / current location) সেই অবস্থানকে byte এককে প্রকাশ করে

rewind()
* বর্তমান অবস্থান থেকে ফাইল অপারেশনকে ফাইলের শুরুতে নিয়ে যায়

rename()
* ফাইলের নাম পরিবর্তনের জন্য

remove()
* ফাইল মুছে ফেলার জন্য

feof()
* ফাইল অপারেশন ফাইলের সমাপ্তিতে পৌছেছে কিনা তা চেক করে

ferror()
* কদাচিৎ ব্যবহার করা হয় কোন ফাইল অপারেশনে error হয়েছে কিনা তা পরীক্ষার জন্য।

*** read হচ্ছে ইনপুট অপারেশন ও write হচ্ছে আউটপুট অপারেশন।
*** fseek() এবং ftell() হচ্ছে র‍্যান্ডম একসেস ফাংশন।

(to be continued....)
FILE I/O (ফাইল ইনপুট / আউটপুট) (continued.. Part 1)

FILE খোলা

একটি নতুন ফাইল তৈরী বা আগে থেকে তৈরী একটি ফাইল কোন ফাইল অপারেশন এর জন্য খোলার জন্য fopen() ফাংশন ব্যবহার করা হয়। এর প্রোটোটাইপ

FILE *fopen(char *filename, char *mode)

এবং ব্যবহারের সাধারণ রূপ হচ্ছে

FILE *fp;
fp = fopen("filename.extension", "mode");

ফাইল খোলার জন্য অপারেটিং সিস্টেমকে আমাদের নিম্নোক্ত তথ্যগুলো জানাতে হয়:

১. ফাইলের নাম (file name)
২. ফাইলের ডেটা স্ট্রাকচার (Data Structure)
৩. ফাইল খোলার কারন (purpose)

ফাইলের নাম হল একটি ক্যারেক্টার স্ট্রিং যাতে ফাইলের নাম থাকে এবং নামের সাথে '.' দ্বারা ফাইল নেম এক্সটেনশন যুক্ত থাকে। ফাইল নেম এক্সটেনশন বাধ্যতামূলক নয়, এটি অপশনাল। তবে ফাইল নেম এক্সটেনশন অপারেটিং সিস্টেম, অপারেটিং সিস্টেমে থাকা বিভিন্ন প্রোগ্রাম ও অনেক সময় ব্যবহারকারীকেও ফাইলের ধরন বুঝতে সাহায্য করে। যেমন

input.txt তে input হচ্ছে ফাইলের নাম এবং .txt হল ফাইল নেম এক্সটেনশন।

একটি ফাইলের data structure, স্ট্যান্ডার্ড I/O লাইব্রেরী stdio.h এ FILE নামক structure হিসাবে ডিফাইন করা আছে। FILE হচ্ছে একটি defined data type। FILE স্ট্রাকচারে ফাইল সম্পর্কে বিভিন্ন তথ্য জমা থাকে। সকল ফাইলকে তৈরী বা খোলার সময় একটি ফাইল পয়েন্টার ডিক্লেয়ার করতে হয় যা মেমরিতে ফাইলটির অবস্থান নির্দেশ করে এবং সেই ফাইল পয়েন্টারের data type হতে হবে অবশ্যই FILE। fopen() এর প্রোটোটাইপ দেখলেই আমরা বুঝতে পারবো যে fopen() ফাংশনও একটি পয়েন্টার রিটার্ন দেয় যার type হচ্ছে FILE।

ফাইল খোলার উদ্দেশ্য বা purpose বলতে বোঝায় ফাইলটি আমরা কি কাজে খুলছি। আমরা কি নতুন ফাইল তৈরী করছি? নাকি আগে থেকেই আছে এমন কোন ফাইলে ইনপুট দিতে চাচ্ছি? নাকি ফাইল থেকে স্রেফ ডেটা read করতে চাচ্ছি? fopen() ফাংশনের mode নামক প্যারামিটার আর্গুমেন্ট হিসেবে আমাদের কাছ থেকে এই উদ্দেশ্য জানতে চায়।

কাজেই mode নিয়ে আলোচনা আবশ্যক। নিচের ছক লক্ষ্য করঃ

"r"
* read করার জন্য ফাইল খোলা নির্দেশ করে। যদি ফাইল নেম এ উল্লেখ করা ফাইলটি আগে থেকেই না থাকে তাহলে এই mode এ fopen() কাজ করতে পারে না এবং NULL রিটার্ন করে। NULL হচ্ছে একটি invalid মেমরি এড্রেস।

"w"
* write করার জন্য ফাইল খোলা নির্দেশ করে।
* ফাইল নেম এ উল্লেখ করা ফাইলটি আগে থেকেই না থাকলে ফাইল টি নতুন করে তৈরী করা হয়।
* ফাইল নেম এ উল্লেখ করা ফাইলটি আগে থেকেই থাকলে ফাইলের সব ডেটা মুছে ফেলে write করার জন্য প্রস্তুত করা হয়।

"a"
* ফাইলে নতুন ডেটা append বা add করা নির্দেশ করে
* ফাইল নেম এ উল্লেখ করা ফাইলটি আগে থেকেই না থাকলে ফাইলটি তৈরী করা হয়
* ফাইল নেম এ উল্লেখ করা ফাইলটি আগে থেকেই থাকলে যে পর্যন্ত ফাইলে তথ্য আছে তার পর থেকে ফাইলে write অপারেশন চালানো হয়। পূর্বের ডেটা অপরিবর্তিত থাকে।

"r+" ও "w+"
* ফাইলকে read এবং write উভয়ই করা যায়
* তবে "r+" এর ক্ষেত্রে ফাইলটি আগে থেকে তৈরী না থাকলে নতুন করে ফাইলটি তৈরী করবে না, error দেখাবে।
* "w+" এর ক্ষেত্রে ফাইলটি আগে থেকে তৈরী না থাকলে নতুন করে ফাইলটি তৈরী করবে, এবং তৈরী থাকলে আগের সব ডেটা মুছে ফেলবে।

"a+"
* "a" এর মতই, শুধু read ও write উভয়ই করা যাবে

আমরা বলেছিলাম যে স্ট্রীম ২ ধরনের। টেক্সট ও বাইনারি। উপরের mode গুলো টেক্সট ডেটার জন্য। বাইনারি ডেটার জন্য সমতুল্য মোড গুলো হচ্ছে:

"rb" → "r"
"wb" → "w"
"ab" → "a"
"r+b" বা  "rb+" → "r+"
"w+b" বা "wb+" → "w+"
"a+b" বা "ab+" → "a+"

কাজেই একটি ফাইল তৈরী বা খোলার জন্য-

১. একটি ফাইল পয়েন্টার ডিক্লেয়ার করতে হবে যার data type হবে FILE
২. ফাইলের নাম ও mode কে আর্গুমেন্ট হিসেবে নিয়ে fopen() ফাংশন কল করতে হবে যা একটি ফাইল পয়েন্টার রিটার্ন করবে।
৩. প্রাপ্ত ফাইল পয়েন্টার টিকে ডিক্লেয়ার করা ফাইল পয়েন্টারে এসাইন করতে হবে।

উদাহরণস্বরূপ, আমরা যদি input.txt ফাইলটিকে নতুন করে তৈরী করে তাতে কিছু ডেটা write করতে চাই তাহলে

FILE *fp;
fp=fopen("input.txt","w");

কোডটি লিখতে হবে।

আবার যদি এমন হয় যে input.txt আগে থেকেই ছিলো এবং আমরা শুধু তার ডেটা read করতে চাই, তাহলে কোড হবে

FILE *fp;
fp=fopen("input.txt","r");

আমরা আলোচনার শুরুতেই বলেছি যে fopen()  ফাইল সফল ভাবে খুলতে না পারলে NULL রিটার্ন করবে। NULL হচ্ছে একটি invalid মেমরি এড্রেস। তাই fopen() ব্যবহার করে অন্য কোন ফাইল অপারেশন শুরু করার আগেই আমাদের চেক করে দেখা উচিত যে ফাইলটি সফলভাবে খোলায় কোন সমস্যা হয়েছে কিনা (অর্থাৎ পয়েন্টার হিসেবে NULL রিটার্ন হয়েছে কিনা)। এইজন্য আমরা লিখতে পারি

FILE *fp;
fp=fopen("input.txt","w");
if (fp==NULL)
printf("can not open file.\n");

অথবা

FILE *fp;
if ((fp=fopen("input.txt","w"))==NULL)
printf("can not open file.\n");

(to be continued...)

File I/O (ফাইল ইনপুট/আউটপুট) (continued.. Part 2)

ফাইলে ইনপুট / আউটপুট অপারেশন

Simple:
ইনপুট ফাংশন→ getc()/fgetc()/getw()
আউটপুট ফাংশন→ putc()/fputc()/putw()

High level:
ইনপুট ফাংশন→ fscanf()/fgets()
আউটপুট ফাংশন→ fprintf()/fputs()

Binary:
ইনপুট ফাংশন→ fread()
আউটপুট ফাংশন→ fwrite()

আজকের আলোচনায় কেবল simple ইনপুট আউটপুট  cover করা হবে। High level ফাংশন ও বাইনারি ইনপুট আউটপুট পরে আলোচিত হবে।

ফাইল খোলার পরে ফাইলে ইনপুট আউটপুট অপারেশন চালানো যায়। এখন থেকে ইনপুট অপারেশন কে read ও আউটপুট অপারেশন কে write বলা হবে।

সবচাইতে সিম্পল ইনপুট আউটপুট ফাংশন হচ্ছে getc()/fgetc() ও getw() ইনপুট এর জন্য এবং putc()/fputc() ও putw() আউটপুট অপারেশন এর জন্য। getc() ও fgetc() একই ফাংশন, ঠিক তেমনি putc() ও fputc()।

getchar() ও putchar() এর মত getc() ও putc() একটিমাত্র ক্যারেকটার নিয়ে কাজ করে। getc() ফাইল থেকে একটি মাত্র ক্যারেকটার read করে এবং putc() ফাইলে একটি মাত্র ক্যারেকটার write করে। getw() ও putw(), getc() ও putc() এর মতই, শুধু এরা একটি ইন্টিজার ভ্যালু read ও write করে।

getc()/fgetc() এর ফাংশন প্রোটোটাইপ হচ্ছে

int getc ( FILE *fp);

putc()/fputc() এর ফাংশন প্রোটোটাইপ হচ্ছে

int putc (int ch, FILE *fp);

putc(), ch ভ্যারিয়েবল এ উপস্থিত ক্যারেকটার টিকে *fp দ্বারা নির্দেশিত ফাইলে write করবে। ঠিক তেমনি যদি কোন char type এর ভ্যারিয়েবল এ getc() কে এসাইন করা হয় তাহলে, getc(), *fp দ্বারা নির্দেশিত ফাইলের প্রথম ক্যারেকটার টিকে read করবে এবং তার ভ্যালু char type এর ভ্যারিয়েবলটিতে জমা করবে। যেমন:

#include<stdio.h>

int main()
{
/* ফাইল পয়েন্টার ও একটি char ভ্যারিয়েবল ডিক্লেয়ার করা হল*/
FILE *fp;
char ch;

/* ফাইল পয়েন্টারে একটি ফাইল input.txt খোলা হল write করার জন্য। ফাইলটি সঠিকভাবে খোলা হল কি না তাও চেক করা হল*/
if((fp=fopen("input.txt", "w")==NULL)
   printf("can not open file.\n");

/* কিবোর্ড দ্বারা একটি ক্যারেকটার ইনপুট দেওয়া হল। এই ক্যারেকটার টিকে putc() ফাংশন ব্যবহার করে input.txt ফাইলে write করা হল*/
ch = getchar();
putc(ch, fp);

......
......
return 0;
}

উপরের কোডটি input.txt নামে একটি ফাইল write করার জন্য খুলবে এবং কিবোর্ড থেকে একটি ক্যারেক্টার ইনপুট নিয়ে তাকে ফাইলে write করবে।

আবার,

#include<stdio.h>

int main()
{
/*ফাইল পয়েন্টার ও একটি char ভ্যারিয়েবল ডিক্লেয়ার করা হল*/
FILE *fp;
char ch;

/* ফাইল পয়েন্টারে read করার জন্য input.txt ফাইলটি খোলা হল এবং সঠিক ভাবে খোলা হল কি না তা চেক করা হল*/
if((fp=fopen("input.txt", "r")==NULL)
    printf("can not open file.\n");

/*getc() ফাংশন ব্যবহার করে ফাইলের প্রথম ক্যারেকটার টি read করা হল এবং তাকে char ভ্যারিয়েবল এ এসাইন করে প্রিন্ট করে দেখা হল যে read সফল হয়েছে*/
ch = getc(fp);
putchar(ch);

.......
.......
return 0;
}

উপরের কোডটি input.txt ফাইলটি read করার জন্য খুলবে এবং একটি ক্যারেক্টার read করে ভ্যারিয়েবল ch এ জমা করবে এবং তাকে স্ক্রীনে প্রিন্ট করে দেখাবে।

fp একটি ফাইল পয়েন্টার। তারমানে এটি একটি মেমরি এড্রেস মাত্র। প্রতি read ও write অপারেশন এর পরে fp এর মান এক ক্যারেক্টার পরিমান বৃদ্ধি পায়। তাই আমরা যদি লুপ ব্যবহার করে ক্রমাগত ইনপুট নিতে থাকি getc() এর মাধ্যমে বা ক্রমাগত আউটপুট দিতে থাকি putc() এর মাধ্যমে তাহলে একই ক্রমে ফাইলে read বা write চলতে থাকবে। তবে-

EOF দ্বারা ফাইলের সমাপ্তি বোঝানো হয়। এটি ফাইলের শেষ প্রান্ত নির্দেশ করে। windows OS এ Ctrl-Z দ্বারা EOF লিখা যায় (Ctrl-Z চাপলে স্ক্রিনে ^z প্রিন্ট হয়)। C ল্যাংগুয়েজ এ EOF হচ্ছে একটি ঋণাত্মক ইন্টিজার (সাধারণত -1)। getc() ব্যবহার করার সময় getc() ততক্ষণ পর্যন্ত read করতে পারে যতক্ষন ফাইলের সমাপ্তিতে না পৌছানো হয়। ফাইলের সমাপ্তিতে পৌছালে getc() রিটার্ন ভ্যালু হিসাবে EOF রিটার্ন করে।

উদাহরণস্বরূপ :

FILE *fp;
char ch;

if((fp=fopen("input.txt","w")==NULL)
    printf("can not open file.\n");

while ((ch=getchar())!= EOF)
    putc(ch, fp);

উপরের কোডটি যতক্ষন পর্যন্ত কিবোর্ডে Ctrl-z চাপা না হবে ততক্ষন পর্যন্ত কিবোর্ড থেকে ইনপুট নিয়ে input.txt ফাইলে write করতে থাকবে।

আবার,

FILE *fp;
char ch;

if((fp=fopen("input.txt", "r")== NULL)
    printf("can not open file. \n");

while ((ch= getc(fp))!= EOF)
    printf("%c", ch);

উপরের কোডটি input.txt ফাইলটি প্রথম থেকে শেষ পর্যন্ত read করে স্ক্রিনে আউটপুট দিবে।

getc() ফাংশন সঠিক ভাবে কাজ না করতে পারলে বা ফাইলের শেষ প্রান্তে পৌছালে EOF রিটার্ণ করে।
putc() ফাংশন সঠিকভাবে কাজ না করলে EOF রিটার্ন করে।

ফাইল বন্ধ করা

কোন ফাইলকে খোলা হলে তাতে অপারেশন শেষ করার পরে ফাইলটিকে বন্ধ করতে হয়। এটি খুবই গুরুত্বপূর্ণ। কারন ফাইল কে অপারেশন শেষে বন্ধ না করলে ডেটা লস হওয়ার সম্ভাবনা খুব বেশি।

ফাইলকে বন্ধ না করে প্রোগ্রাম শেষ করা যাবে না। read মোডে যে ফাইল খোলা হয়েছে, তাকে বন্ধ না করে তাতে write অপারেশন চালানো যাবে না। অর্থাৎ এক মোডে খোলা একটি ফাইলে অন্য কোন ধরনের অপারেশন চালাতে হবে ফাইলটিকে প্রথমে বন্ধ করতে হবে এবং তারপর যথাযথ মোডে ফাইলটি আবার খুলতে হবে।

ফাইল বন্ধ করার জন্য fclose() ব্যবহার করা হয়। fclose() এর ফাংশন প্রোটোটাইপ হচ্ছে

int fclose(FILE *fp);

fclose() সঠিকভাবে সম্পন্ন না হলে EOF রিটার্ণ আসে অন্যথায় শূন্য রিটার্ন আসে।

নিচের উদাহরনে আমরা test.txt ফাইলটি তৈরী করে তাতে কিবোর্ড থেকে ইনপুট নিয়ে write করবো এবং এরপরে read করে স্ক্রিনে আউটপুট দিয়ে দেখবো যে আমাদের ইনপুট ঠিক হয়েছে কি না। কিবোর্ডের ইনপুট তখন শেষ হবে যখন আমরা Ctrl-z চাপবো।

#include<stdio.h>

int main()
{
FILE *fp;
char ch;

fp=fopen("test.txt", "w");

while ((ch= getchar())!= EOF)
    putc(ch, fp);

fclose(fp);

fp=fopen("test.txt", "r");

while ((ch=getc(fp))!= EOF)
    printf("%c", ch);

fclose(fp);

return 0;
}

উপরের প্রোগ্রামে কোন ধরনের error checking করা হয় নি। error checking সহ প্রোগ্রামটি হবে নিম্নরূপ। exit() ফাংশনটি হচ্ছে তৎক্ষণাৎ প্রোগ্রাম থামিয়ে দেওয়ার জন্য।

#include<stdio.h>

int main()
{
FILE *fp;
char ch;

if ((fp=fopen("test.txt", "w")==NULL)
{
    printf("error opening file.\n");
    exit(1);
}
while ((ch= getchar())!= EOF)
    putc(ch, fp);

if(fclose(fp)==EOF)
{
    printf("error closing file.\n");
    exit(2);
}

if ((fp=fopen("test.txt", "r")==NULL)
{
    printf("error opening file.\n");
    exit(1);
}

while ((ch=getc(fp))!= EOF)
    printf("%c", ch);

if(fclose(fp)==EOF)
{
    printf("error closing file.\n");
    exit(2);
}

return 0;
}

0 comments:

Post a Comment