File I/O در لوا :
همونطور که مشخص است ، این عبارت ، مخفف File Input Output به معنای ورودی و خروجی فایل هست و توابع این بخش دست ما رو برای خواندن از یک فایل یا نوشتن روی آن باز میگذارد
اما قبل از پرداختن به موضوع File I/O ، درباره ی عملگر : (عملگر کلون یا colon) در لوا میپردازیم که یکی از کاربردهای آن در شی گرایی در لواست و یک بخش کوچچکی از شی گرایی در لوا را که به این عملگر مربوط است توضیح میدهیم :
به این کد دقت کنید :
اگر ساختار بالا برایتان گنگ هست ، پس حتما اول مبحث ماژول ها را مطالعه بفرمایید و بعد به این مبحث وارد شوید (چون تا آخر یکی از بحث ها همین ماژول و فراخوانی آن هست در File IO) ولی به اختصار ، ماژول ، به عضوی از آرایه که بصورت تابع یا متغییر تعریف میشود ، میگویند و فراخوانی آن با علامت دات (نقطه) ما بین اسم آرایه و اسم تابع یا متغییر هست. تابع بالا را هم به شکل زیر هم میشود نوشت و فرقی با هم ندارند :کد:Account = {balance = 0,x=3} function Account.withdraw(v) Account.balance = Account.balance - v return Account.balance end a = Account; Account = nil Dialog.Message("Notice", a.withdraw(100), MB_OK, MB_ICONINFORMATION, MB_DEFBUTTON1);
اگر در هر ماژولی ، نام آرایه ی اصلی آن (منظور عضوهای داخلی نیست. منظور در مثال بالا ، بصورت مستقیم ، آرایه ی Account است نه عضوهای آن) برابر پوچ یا nil قرار داده شود ، هر کجا برای فراخوانی ، نام آرایه ای که برابر nil قرار گرفت فراخوانی شود (در کد بالا همان نام آرایه ی Account) ، چون مقدارش پوچ است پس ارور میدهد و اعضای آن فراخوانی نمیشوند به این ترتیب ، در کد بالا :کد:Account = {balance = 0,x=3,withdraw=function (v) Account.balance = Account.balance - v return Account.balance end} a = Account; Account = nil Dialog.Message("Notice", a.withdraw(100), MB_OK, MB_ICONINFORMATION, MB_DEFBUTTON1);
اولا دیگر نمیشود عضوهای آن (متغییرهای balance و x) بصورت مستقیم در دسترس باشند مگر اینکه قبلا نام آن آرایه رو در متغییر دیگری ریخته باشیم . پس در کد بالا چون در خط 9 ، چون آرایه ی اصلی آنرا که Account بود را برابر nil گرفتیم بنابراین از آن خط به بعد هیچ گاه نمیتوانیم عضوهای آن آرایه را این گونه بصورت مستقیم فراخوانی کنیم و بنویسیم :
چون این نوع فراخوانی مثل این است که آرایه ای را تعریف نکرده ، عضوهای آن را فراخوانی کنیمکد:Account.x Account.balance Account.withdraw(100)
و اگر در خط 8 ، آرایه ی Account را در متغییر دیگری نمیریختیم ، دیگر نمیشد از عضوهایش استفاده کرد و از این به بعد همانطور که مشخص هست ، باید اعضای این آرایه را بصورت غیر مستقیم فراخواند و چون آرایه ی Account را در متغییر a ریختیم ، از این به بعد عضوهای این آرایه بصورت غیر مستقیم فقط با فراخوانی متغییر a در دسترس اند یعنی باید این گونه فراخوانی کنیم :
خوب حالا موقع فراخوانی تابع میرسد . میبینیم که با فراخوانی :کد:a.x a.balance
با وجود اینکه بصورت غیر مستقیم تابع مان فراخوانی شد ، باز هم ارور میدهد و تابع فراخوانی نمیشود . چرا؟کد:a.withdraw(100)
اگر نگاهی به بدنه ی تابع مان (منظور سر یا هدر تابع نیست) بیاندازید ، میبینید که در بدنه ی تابع ، آرایه ی Account فراخوانی شد و چون این مقدار را برابر nil کرده بودیم ، پس مشخص هست به این دلیل است که ارور میدهد همانطور که برای فراخوانی عضوهای قبلی این آرایه ، وقتی بصورت مستقیم فراخوانی میکردیم ، ارور میداد.
اما چاره چیست؟
در این گونه مواقع همانطور که قبلا بجای آرایه ی Account ، متغییری که آرایه ی Account را که در آن ریخته بودیم (متغییر a) را جایگزین اش میکردیم یک ورودی یا پارامتری برای این تابع ساخته و در بدنه ی این تابع هم مثل قبل ، جایگزین آن میکنیم (به هدر تابع که Account.withdraw هست فقط کاری نداریم) یعنی این گونه کد را اصلاح میکنیم :
یعنی چون در بدنه ی تابع مان Account آمد و این آرایه هم پوچ بود ، باید این Account را در بدنه ی اصلی تابع ، جایگزین میکردیم و دقیقا مثل قبل جایگزین آن متغییری که آن آرایه را در آن ذخیره کرده بودیم پس باید در فراخوانی، در پارامتر به عنوان ورودی ، مثل قبل ، همان متغییر a را که محتوای آرایه ی Account در آن ذخیره بود را بفرستیم تا بجای Account ، این پارامتر را جایگزین آن کنیمکد:Account = {balance = 0,x=3} function Account.withdraw(ArrayPar,v) ArrayPar.balance = ArrayPar.balance - v return ArrayPar.balance end a = Account; Account = nil Dialog.Message("Notice", a.withdraw(a,100), MB_OK, MB_ICONINFORMATION, MB_DEFBUTTON1);
پس در فراخوانی :
بجای Account ، متغییر a را به عنوان اولین آرگومان که جایگزین اولین آرگومان تابع که ArrayPar هست ، فرستادیم برای آنکه در بدنه ی تابع اصلی ، آرایه ی Account بصورت مستقیم فراخوانی نشودکد:a.withdraw(a,100)
آرگومان ArrayPar در تابع بالا ، معروف به آرگومان self یا this هستند و مظهر و نشانه ی این هستند که بجای هر عضوی از آرایه یا حتی خود آرایه (مثل مثال بالا) که nil شد ، به عنوان پارامتر جایگزین در بدنه ی تابع ، مشکل اختلال تابع را حل کنند
خوب اما نقش عملگر کلون : (colon) چیست؟
هر گاه در تابعی به عنوان ماژول (تابع در آرایه) مثل کد بالا ، به هر دلیل (دلیل بالا ، nil شدن مقدار Account بود) نیاز شد که پارامتری را به عنوان نام آرایه (یا ماژول ...) را بصورت مخفیانه بفرستیم ، از علامت کلون یعنی این علامت : استفاده میکنیم. مثلا بجای فراخوانی تابع بصورت :
به این صورت فراخوانی کنیم :کد:a.withdraw(a,100)
یا بجای نوشتن هدر تابع (به هدر تابع دقت کنید نه به بدنه اش) ، به این صورت :کد:a:withdraw(100)
این گونه بنویسیم :کد:function Account.withdraw(ArrayPar,v) ArrayPar.balance = ArrayPar.balance - v return ArrayPar.balance end
یعنی کد بالا را میتوانیم به این صورت بنویسیم :کد:function Account:withdraw(v) self.balance = self.balance - v return self.balance end
به فراخوانی تابع a:withdraw(100) در خط آخر دقت کنید که همان فرقی با فراخوانی a.withdraw(a,100) نداردکد:Account = {balance = 0,x=3} function Account.withdraw(ArrayPar,v) ArrayPar.balance = ArrayPar.balance - v return ArrayPar.balance end a = Account; Account = nil Dialog.Message("Notice", a:withdraw(100), MB_OK, MB_ICONINFORMATION, MB_DEFBUTTON1);
به عبارتی دیگر هر گاه خواستیم سمت چپ عملگر : (که در فراخوانی بالا ، سمت چپ عملگر : همان متغییر a هست) را بصورت مخفیانه به عنوان پارامتر تابع بفرستیم ، از این عملگر استفاده میکنیم یعنی هر کجا که علامت کلون استفاده شد در پارامتر آن ، آرایه یا متغییری که در کد بالا استفاده میکردیم ، حذف میکنیم .حالا نکته اینجاست که اگر در موقع فراخوانی تابع از علامت کلون استفاده کردیم فقط میتوانیم این پارامتر را از قسمت فراخوانی این تابع حذف کنیم و دیگر نباید این پارامتر را موقع هدر و تعریف تابع حذف کرد . یعنی :
در کد بالا ، چون فقط در خط آخر یعنی موقع فراخوانی تابع ، از علامت کلون استفاده کردیم پس فقط میتوانیم از همین قسمت این پارامتر را که قبلا به عنوان پارامتر a جایگزین کرده بودیم ، حذف کنیم ولی در قسمت تعریف تابع چئت از این علامت استفاده نکردیم ، پس نباید این پارامتر را از آنجا حذف کنیم . عکس این گفته هم صادق استکد:Account = {balance = 0,x=3} function Account.withdraw(ArrayPar,v) ArrayPar.balance = ArrayPar.balance - v return ArrayPar.balance end a = Account; Account = nil Dialog.Message("Notice", a:withdraw(100), MB_OK, MB_ICONINFORMATION, MB_DEFBUTTON1);
نکته ی مهمی که در قضیه ی کلون است ، این است که همونطور که قبلا گفته شد ، این پارامتر فقط بنام self یا this فقط در تابع شناخته میشود البته در صورتی که مخفی باشد یعنی از علامت کلون استفاده شود پس اگر در تعریف تابع بالا یعنی Account.withdraw ، اگر از علامت کلون استفاده کردیم و مجبور شدیم که ArrayPar رو پاک کنیم از پارامترش ، حالا باید در بدنه ی تابع ، بجای متغییر ArrayPar از نام متغییر self (همه ی حروف ها کوچک) استفاده کنیم یعنی :
کد:Account = {balance = 0,x=3} function Account:withdraw(v) self.balance = self.balance - v return self.balance end a = Account; Account = nil Dialog.Message("Notice", a:withdraw(100), MB_OK, MB_ICONINFORMATION, MB_DEFBUTTON1);
حالا به مبحث اصلی مان که File I/O هست میپردازیم :
نکته ی بسیار مهمی که همان ابتدای کار باید بیان شود این است که برای استفاده (خواندن یا نوشتن) از یک فایل ، ابتدا باید آن فایل را برای قصد مورد نظر (منظور برای خواندن یا اینکه برای نوشتن است) باید باز شود و نکته ی مهم دیگر این است که وقتی کارمان تمام شد ، باید آن فایل را ببندیم. (منظور از باز کردن و بستن ، دابل کلیک روی فایل نیست . بلکه با توابع file i/o لوا باید این کار را انجام داد که در ادامه عرض میشود)
دقت کنید باز کردن یک فایل به معنای خواندن محتوای آن نیست . باز کردن فایل هم با تابع io.open در لوا انجام میگیرد . توضیحات این توابع بصورت انگلیسی در لینک زیر موجود است که در ادامه به بررسی مهم ترین این توابع میپردازیم :
Lua 5.1 Reference Manual
در فرآیند باز کردن یک فایل ،باید به این چند نکته هم توجه کرد :
اول و مهمترین آن اینکه میخواهیم محتوای فایل مورد نظر را بخوانیم یا بنویسیم . همانطور که گفته شد ، قبل از هر عملیات خواندن یا نوشتن ، ابتدا باید فایل را باز کرد اما نکته ی حائز اهمیت اینجاست که باز کردن فایل برای خواندن و باز کردن فایل برای نوشتن متفاوت است یعنی اگر قصد خواندن یک فایل را داریم ، قبل از خواندن ، باید فایل مورد نظر را برای خواندن باز کنیم و اگر قصد نوشتن آنرا داریم ، قبل از آن باید فایل را برای نوشتن باز کنیم . دقت کنیم که اگر قصد نوشتن فایل را داشته باشیم و قبل از آن ، فایل را برای خواندن باز کرده باشیم ، عملیات نوشتن روی آن فایل انجام نمیشود و برعکس اش هم صادق است
دوم آنکه موقع باز کردن فایل ، مشخص کنیم که آن فایل را برای حالت باینری میخواهیم باز کنیم (جهت خواندن یا نوشتن) و یا حالت متن معمولی . برای تشخیص اینکه یک فایلی به حالت باینری است یا متن معمولی ، آنرا با notpad باز کنید و اگر محتوای آن فقط و فقط حاوی متن قابل تشخیص بود ، بصورت متن عادی آن فایل ذخیره شده است و اگر غیر از این بود یعنی حاوی کاراکترهای ناشناخته و درهم بود ، (که معمولا غیر از فایل های txt ، بقیه این طور هستند) آن فایل بصورت باینری هست و برای باز کردن آن (جهت خواندن یا نوشتن آن فایل) صورت باینری باز شود. مشخص هست که اگر با توابع باینری لوا مثل تابع string.dump اگر کار میکنیم ، برای ذخیره کردن و نوشتن در داخل فایل ، باید آن فایل را بصورت باینری برای نوشتن باز کنیم
دقت کنید که عملیات باز کردن حتی برای فایلی که از قبل وجود ندارد و تازه میخواهیم آن فایل را بنویسیم و تولید کنیم هم باید انجام شود
در توابع File I/O ، دو دسته توابع وجود دارند که در واقع فرق شان در علامت کلون یا علامت : است . آن توابعی (در واقع ماژول) که بصورت عادی یا علامت دات (نقطه) دارند مثل io.input و ... ، معمولا مسیر فایل مورد نظر را بصورت پارامتر تابع دریافت میکنند مثلا در همین
هست که باید بجای آرگومان ورودی file ، مسیر کامل فایل را بدهیمکد:io.input ([file])
اما در توابعی که علامت کلون یا : را دارند ، همانطور که در بالا گفته شد ، هر گاه این علامت بیاید ، آرگومان خاص و از پیش تعیین شده ای را که در اینجا همان مسیر فایل بجای آرگومان file در بالاست ، از آرگومان ورودی این نوع توابع حذف میشوند و بجایش این پارامتر را (که مسیر فایل را مشخص میکرد) به عنوان هندل فایل (نه به عنوان رشته ای به عنوان مسیر فایل) قبل از علامت کلون یا : میگیرند . یعنی در تابعی مثل
متغییر file (که قبل از علامت کلون : آمده است) یک متغییریست که بجای رشته ی مسیر فایل (که به عنوان پارامتر ورودی تعریف میشد در توابع بدون کلون) ، هندل که شماره ای خاص از آن فایل است را میگیرد . هندل هم در ادامه بیشتر میپردازیم اما فعلا همین قدر کافی است که تابع io.open علاوه بر باز کردن (منظور خواندن و نوشتن نیست) فایل مورد نظر ، هندل آنرا برمیگرداند که میتوانیم از متغییری که از این تابع برگردانده میشود ، در تابعی با علامت کلون مثل file:read قبل از متغییر کلون که file بود ، بجایش استفاده کنیمکد:file:read()
حالا به بررسی توابع File I/O بپردازیم :
همانطور که گفته شد ، توابع file i/o به دو دسته ی عملگر کلون دار و بدون کلون تقسیم میشوند اما توابع با کلون ، به مانند توابع بدون کلون ، کامل نیستند اما چون توابعی که علامت یا بهتر بگوییم عملگر کلون دارند ، فقط هندل فایل را میخواهند و مسیر فایل به عنوان آرگومان از آنها حذف شد ، پس کار کردن با آنها راحت تر است و ما اغلب روی این توابع کلون دار کار میکنیم تا جایی که لازم باشد
نکته اینکه در هر کجا وقتی آرگومانی را داخل علامت کلوشه یا [ ] نمایش دادند ، یعنی آن آرگومان قرار دادن یا ندادنش اختیاریست و اگر قرار ندهیم ، مقداری پیش فرض را خود زبان لوا قرار میدهد
تابع io.open
همانطور که درباره ی این تابع در بالا تقریبا توضیح کامل داده شد (حتما خطوط بالا را مطالعه بفرمایئد) ، این تابع وظیفه ی باز کردن یک فایل جهت خواندن یا نوشتن را بر عهده دارد و قبل از عملیات خواندن و نوشتن ، باید از این تابع استفاده شود . این تابع ، مقدار عددی هندل آن فایل را برمیگرداند که اغلب در توابع کلون دار این مقدار بکارمان میآیدکد:io.open (filename [, mode])
همانطور که مشخص است ، آرگومان filename ، مسیر فایلی که میخواهیم آنرا باز کنیم را باید بدهیم پس آرگومانی رشته ای است
آرگومان دوم mode همانطور که گفته شد چون داخل علامت کلوشه [ ] هست پس اختیاری است و بصورت پیش فرض اگر مقداری ندهیم ، مقدار "r" را میدهد. این آرگومان رشته ایست و نوع باز کردن یک فایل را از ما میخواهد و باید یکی از پارامترهای زیر را به عنوان رشته به آن داد :
رشته "r" : همانطور که قبلا گفته شد ، اگر قصد داریم محتوای یک فایل را بخوانیم ، قبل از آن باید آن فایل را برای خواندن باز کنیم که این رشته در آرگومان دوم همین کار را انجام میدهد اما اگر میخواهیم در فایل چیزی بنویسیم ، باید آن فایل را برای نوشتن باز کنیم و نمیشود فایلی را برای نوشتن باز کنیم و برای خواندن آنرا باز نکنیم اما محتوای آن فایل را بتوانیم بخوانیم (دقت کنیم ، باعث نمیشود که فایل خوانده شود . فقط به منظور خواندن ، آنرا باز میکند)
رشته "w" : فایل را برای نوشتن باز میکند (دقت کنیم ، باعث نمیشود که فایل خوانده شود . فقط به منظور خواندن ، آنرا باز میکند)
رشته "a" : برای نوشتن یا خواندن ، فایل را از ادامه باز میکند و میشود مثلا فایل را از ادامه ی آخرین سطر نوشت اما مثلا "w" فایل را از ابتدا اور رایت میکند و ...
یک رشته ی دیگر هم هر کدام یک علامت + در آخر میگیرند که برخلاف توضیحات سایتش ، مثل حالت عادی باعث اوررایت میشود (چیزی که باعث آپدیت میشود فقط رشته ی "a" هست)
باز هم آخر همه ی این رشته ها را اگر کلمه ی b بنویسیم یعنی اگر بنویسیم "rb" یا "wb" یا "ab" یعنی اینکه بجای فایل متن و txt ، میخواهیم فایل باینری را برای خواندن یا نوشتن یا آپدیت کردن ، باز کنیم (فایل باینری هم هر فایلی را که با notepad باز کنیم و غیر از متن ، علامت های درهم نوشته شده باشد را میگویند)
تابع io.open تنها تابع مهم قسمت file i/o است که برایش علامت همچین تابعی با کلون در نظر گرفته نشده . حالا توابع کلون دار
تابع file:lines
اول اینکه همانطور که گفته شد ، قبل از استفاده از این تابع ، باید فایل مورد نظر را در حالت خواندن باز کنیم یعنیکد:file:lines()
اگر فایل مورد نظر بصورت بایتی هست ، باید رشته ی b را به آخر آن اضافه کردکد:io.open (filename , "r")
این تابع ، یک تابع iterator یا تابع تکرار کننده هست که هر بار ، یک خط از فایل مورد نظر را میخواند (تابع iterator قبلا در موضوع جداگانه ای توضیح داده شده است. در حالت کلی تابعی است که عملی را آنقدر انجام دهد که مقدارش nil شود)
همانطور که در تاپیک آموزش iterator ها بررسی شد ، تابع iterator بصورت عادی فراخوانی نمیشود و باید با یک حلقه ی تکرار که حلقه ی for برای آن رایج است ، فراخوانی شود . اگر مایل به نحوه ی عملکرد این تابع هستید ، ابتدا آن آموزش را مطالعه بفرمائید . این تابع هم این گونه فراخوانی میشود :
متغییر OneLine ، در هر بار تکرار ، یک خط از فایل خوانده شده را برمیگرداند . حالا متغییر OneLine دلخواهی است و میتواند هر متغییری که خودتان تعریف میکنید باشدکد:for OneLine in file:lines() do --body end
این تابع آنقدر تکرار میشود که تمام خط های فایل مورد نظر تکرار شود
تابع file:read
اول اینکه همانطور که گفته شد ، قبل از استفاده از این تابع ، باید فایل مورد نظر را در حالت خواندن باز کنیم یعنیکد:file:read(···)
اگر فایل مورد نظر بصورت بایتی هست ، باید رشته ی b را به آخر آن اضافه کردکد:io.open (filename , "r")
تابع بالا ، فایل را بر اساس چیزی که ما میگوییم ، میخاند . اینکه بر چه اساسی بخواند ، یک آرگومان به عنوان ورودی به این تابع میدهیم که مشخص کننده ی نوع خواندن آن است . این ورودی ممکن است رشته یا عدد باشد که در ادامه توضیح میدهیم :
رشته ی. دقیق کار این آرگومان مشخص نیست اما در توضیحات سایت آمده که عدد را میخواند و عدد را برمیگرداندکد:"*n"
رشته یکه یکی از مهمترین آرگومان ورودی این تابع است ، باعث میشود این تابع کل فایل را بخواند و کل متن آنرا برگرداند (دقت کنید اگر فایل بزرگ باشد (مثلا فایل ویدئویی بالای 200 مگ و ...) ، این تابع نمیتواند کل متن فایل را در داخل یک متغییر بریزد و در واقع نمیتواند کل فایل را یک دفعه بخواند . در این مواقع باید با تابع iterator ای که در بالا توضیح داده شد file:lines ، خط به خط فایل را جداگانه بخوانیم یا از آرگومان دیگری که در ادامه برای این تابع توضیح داده میشود استفاده کنیم).کد:"*a"
رشته ی [CODE]["*l"/CODE] طبق توضیحات سایت لوا ، باعث خوانده شدن خط بعدی میشود
در آرگومان دیگر از همین تابع file:read ، بجای رشته های بالا ، اگر عدد وارد کنیم ، این تابع فقط همان تعداد متن را از ابتدای آن فایل میخواند . مثلا اگر بنویسیمباعث میشود 20 کاراکتر اول از آن خط خوانده شود . برای اینکه فایل هر تعداد کاراکتر خاص را پی در پی بخواند یعنی مثلا هر 20 کاراکتر 20 کاراکتر را بخواند ، مشخص است که باید از یک حلقه ی تکرار با این تابع استفاده کنیم و یا تابع iterator این کار را کنیمکد:file:read(20)
تابع file:write
اول اینکه همانطور که گفته شد ، قبل از استفاده از این تابع ، باید فایل مورد نظر را در حالت خواندن باز کنیم یعنیکد:file:write(···)
اگر فایل مورد نظر بصورت بایتی هست ، باید رشته ی b را به آخر آن اضافه کردکد:io.open (filename , "w")
هر آرگومانی از نوع عدد یا رشته به تابع بالا بدهیم ، آنرا در فایل مورد نظر مینویسد . برای تبدیل انواع داده ای جز عدد و رشته ، از توابع tostring و string.format استفاده کنید قبل از نوشتن
این تابع میتواند هر تعداد آرگومان داشته باشد که به ترتیب این تابع ، آرگومان ها را پشت سر هم مینویسد
تابع file:close
این تابع هیچ آرگومانی را به عنوان ورودی نمیگیرد و مقداری را هم برنمیگرداند . این تابع باعث بستن فایلی که باز کردیم میشود . اگر فایل مورد نظر را با این تابع نبندیم ، موقع حذف آن ، سیستم پیام file in use را میدهد و تا زمانی که نرم افزار مورد نظر (AMS و ...) را نبندیم و یا گاها سیستم را ریستارت نکنیم ، فایل را نمیتوانیم حذف کنیمکد:file:close()
مثال ها :
1) میخواهیم فایلی را بنام Document.txt در پوشه اصلی (CD_Root) بخوانیم (در فایل Document.txt متن دلخواه را وارد کنید و ذخیره کنید) :
خوب اول باید فایل مورد نظر باز شود پس از تابع io.open استفاده میکنیم و در آرگومان اول آن ، مسیر فایل را میدهیم. در آرگومان دوم چون میخواهیم فایل را بخوانیم ، پس برای خواندن باید آنرا باز کنیم و چون فایل txt هست یعنی متن در هم نیست پس فایل باینری نیست پس در آرگومان دوم آن ، فقط رشته ی "r" را وارد میکنیم (دقت کنید حروف کوچک و بزرگ را) . فایل Document.txt را برای خواندن باز میکند و عددی را به عنوان هندل این فایل ، برمیگرداند پس در متغییر این تابع که FileHandle هست ، هندل این فایل ذخیره میشودکد:FileHandle = io.open("Document.txt", "r"); FileContent=FileHandle:read("*a"); Dialog.Message("Notice", FileContent, MB_OK, MB_ICONINFORMATION, MB_DEFBUTTON1); FileHandle:close();
همونطور که گفته شد ، در توابع های کلون دار که در بالا ذکر شد ، بجای file یعنی قبل از علامت کلون گفتیم که باید از هندل فایلی که از خروجی تابع io.open استفاده میشود (در مثال بالا همان متغییر FileHandle ) استفاده کنیم. یعنی قبل از توابع کلون دار ، در مثال بالا ، باید بنویسیم FileHandle که تابع باید بداند که به کدام فایل این عملیات را انجام دهد (همانطور که گفته شد ، در توابع کلون دار ، مسیر فایل از آرگومان تابع حذف شد و به هندل فایل قبل از علامت کلون انتقال داده شد)
پس برای خواندن فایل باید از تابع FileHandle:read استفاده کنیم . آرگومان رشته ی "*a" در این تابع یعنی اینکه کل محتوای فایل را بخوان و در متغییر خروجی این تابع که FileContent است بریز
خط بعد هم که محتوا را چاپ میکند با پیغام
خط آخر هم که مثل ساختار فایل های کلون که در خط بالا گفته شد باید قبل از علامت کلون ، از هندل فایل استفاده کرد ، پس FileHandle:close() باعث بستن فایلی که هندل FileHandle داشت میشود . دقت کنید قبل از استفاده از از این تابع (مثلا موقعی که فایل خوانده شد و پیام داد در خط سوم) اگر فایل مورد نظر را بخواهیم حذف کنیم ، با پیام file in use ویندوز مواجه میشویم اما بعد از خط آخر میتوانیم حذف آنرا
2) متنی را داخل فایل Document.txt در پوشه اصلی بنویسیم :
اگر دیدید فقط با آوردن رشته ی "Document.txt" عمل نمیکند ، رشته ی کامل مسیر را بصورت دستی بیاورید (بجای UserName ، نام کاربری خود را در رشته ی اول بدهید) . حتی سعی کنید از متغییر _SourceFolder هم استفاده نکنید شاید جواب ندهدکد:FileHandle = io.open("C:\\Users\\UserName\\Documents\\AutoPlay Media Studio 8\\Projects\\file io\\CD_Root\\Document.txt", "w"); FileContent=FileHandle:write("this is a write test into taste file"); FileHandle:close();
چون قصد نوشتن داشتیم پس باید برای نوشتن باز کنیم
دقت کنید در مثال بالا (موقع نوشتن) ، حتی اگر فایل مورد نظر هم وجود نداشته باشد ، ابتدا باید آنرا باز کرد برای نوشتن و بسیار مهم است این نکته
3) در این مثال ، فایل Document.txt در پوشه ی اصلی را خوانده و در پیامی چاپ و بعد از آن متن هایی به آن میخواهیم اضافه کنیم :
بجای UserName ، نام کاربری خود را در رشته ی اول بدهیدکد:Path="C:\\Users\\UserName\\Documents\\AutoPlay Media Studio 8\\Projects\\file io\\CD_Root\\Document.txt"; ReadHandel = io.open(Path, "r"); FileContent=ReadHandel:read("*a"); Dialog.Message("Notice", FileContent, MB_OK, MB_ICONINFORMATION, MB_DEFBUTTON1); WriteHandel = io.open(Path, "a"); WriteHandel:write("\n","this line is one line remain to last line","\n","this line is last line . End") ReadHandel:close(); WriteHandel:close();
اولا چون هم میخواهیم بنویسیم و هم بخوانیم پس باید 2 بار فایل را باز کنیم . یکبار برای خواندن که "r" را در آرگومان مینویسیم و در متغییر ReadHandel ذخیره کردیم پس هر تابعی مربوط به خواندن مثل خط سوم را همراه با این هندل و متغییر میآوریم و یکبار دیگر برای نوشتن هم باید فایل را باز کرد (در خط پنجم) و در متغییر WriteHandel ، هندل این فایل برای نوشتن را ذخیره کردیم . منتها از "w" به عنوان آرگومان این تابع استفاده نکردیم چون میخواستیم ، در ادامه ی آن متن اضافه کنیم و اوررایت نکنیم . اگر از "w" استفاده میکردیم ، متنهای قبلی پاک میشد پس برای آپدیت و اضافه کردن متن ، از رشته "a" بجای "w" در آرگومان این تابع استفاده کردیم
دوما در خط 3 تا مانده یه آخر هم ، باید از هندلی که برای آپدیت کردن در خط 5 داخل متغییر WriteHandel ریختیم ، استفاده کنیم . این تابع ، دانه دانه آرگومان ها را شروع به اضافه کردن در فایل مورد نظر میکند یعنی اول ، آرگومان اول را که باعث اینتر زدن یا رفتن به خط بعد در فایل میشود را مینویسد و بعد آرگومان دوم که یک متن است و سپس آرگوامن بعدی که باز هم به خط بعد میرود و سپس آرگومان آخر را که متنی دیگر است
در دو خط آخر هم که چون 2 بار فایل را باز کردیم ، یعنی یکبار برای خواندن و یکبار هم برای نوشتن ، پس حتما حتما باید 2 بار آنهم هر بار با هندل های مورد نظر (یکبار با هندل خواندن و یکبار هم با هندل نوشتن) فایل را ببندیم پس به این ترتیب ، ReadHandel:close() و WriteHandel:close() را در 2 خط آخر مینویسیم (این 2 خط آخر یکی از نکات بسیار مهم است که نباید فراموش کرد)
4) یک فایل pdf بنام OldPdf در پوشه سورس ایجاد کنید . با کد زیر ، اطلاعات فایل را خوانده و در فایل txt با نام NewPdf میریزد :
بجای UserName ، نام کاربری خود را در رشته ی اول بدهیدکد:PathSource="C:\\Users\\UserName\\Documents\\AutoPlay Media Studio 8\\Projects\\file io\\CD_Root\\OldPdf.pdf"; PathExit="C:\\Users\\UserName\\Documents\\AutoPlay Media Studio 8\\Projects\\file io\\CD_Root\\NewPdf.txt"; FileRead=io.open(PathSource ,"rb"); FileWrite=io.open(PathExit ,"wb"); PdfContent = FileRead:read("*a"); FileWrite:write(PdfContent); FileRead:close(); FileWrite:close();
هر فایل pdf را که با notepad باز کنید ، میبینید که کلمات درهم برهم نوشته شده است پس فایل باینری ست و برای باز کردن (چه برای خواندن یا نوشتن) باید بصورت باینری باز شود پس موقع باز کردن ، در آخر دومین آرگومان ، کاراکتر b را اضافه میکنیم (این بسیار مهم است) یعنی در خط 3 و 4
خط 5 هم کل محتوای فایل pdf را میخواند و در خط بعد هم کل این محتوا را در داخل فایل NewPdf.txt مینویسد (میتوانیم حتی برای این فایل ، هر پسوند دیگر یا حتی بدون پسوند ذخیره کنیم و در نظر بگیریم)
حالا فایل تکست NewPdf را اگر با نرم افزاری که فایل سورس اصلی را که acrobat reader بود اجرا کنید ، میبینید اجرا میشود و نحوه ی کپی کردن اطلاعات یک فایل میتواند این روش باشد . اما دقت کنید که اگر فایل حجیم باشد (مثلا فایل ویدئویی بالای 100 مگ) بسته به میزان رم ، اطلاعات نمیتواند داخل یک متغییر ذخیره شود . در این صورت باید خط به خط اطلاعات باینری فایل مورد نظر خوانده شود و داخل یک آرایه ذخیره شود (اگر از این روش ، یعنی از تابع iterator استفاده و اطلاعات را داخل آرایه بریزیم ، دیگر نمیشود اطلاعات فایل مبداء را مثل کد بالا ذخیره کنیم (ذخیره میشود) اما دیگر کدهای ذخیره شده در فایل قابل استفاده نیست و فایل اجرا نمیشود. کلا برای اجرای فایل ، ظاهرا یک روش وجود دارد و آنهم خواندن یکباره ی کل فایل و ذخیره در فایل جدید است)
5) کد زیر ، محتوای فایل OldFile را خط به خط میخواند :
در خط 5 ، تابع تکرار کننده (iterator) ی FileRead:lines را اجرا و هر بار ، هر خط را در متغییر Line ذخیره میکندکد:PathSource="C:\\Users\\UserName\\Documents\\AutoPlay Media Studio 8\\Projects\\file io\\CD_Root\\OldPdf.pdf"; NewFile={} Counter=1; FileRead=io.open(PathSource ,"rb"); for Line in FileRead:lines() do NewFile[Counter]=Line; Dialog.Message("FileLine", NewFile[Counter], MB_OK, MB_ICONINFORMATION, MB_DEFBUTTON1); Counter=Counter+1; end FileRead:close();
همانطور که گفته شد ، اگر با این روش ، اطلاعات خوانده شده را در فایلی ذخیره کنیم ، آن فایل قابلیت اجرا ندارد و تنها روش برای این کار ، خواندن یکباره ی کل محتویات فایل است
حالا به قضیه ی توابع file i/o هایی که بدون علامت کلون هستند میپردازیم :
همانطور که گفته شد ، توابع بدون کلون ، مسیر فایل یا هندل فایل را بجای اینکه قبل از علامت کلون دریافت کنند ، داخل آرگومان و ورودی توابع شان دریافت میکنند
تابع io.input
بعد از تابع io.open و باز کردن فایل ، تابع io.input باعث میشود اگر در تابع ای که مربوط به عمل خواندن میشود (از این به بعد هر جایی تابع گفتیم ، منظور تابع file i/o بدون عملگر کلون است) ، مسیر فایل را ذکر نکنیم ، اتوماتیک هندل و مسیر این فایل که داخل آرگومان file که از هندل خروجی تابع io.open داده بودیم را ست میکند در غیر این صورت ، باید در همه ی آرگومان های بقیه ی توابع ، هر کجا آرگومانی بنام file را دیدیم ، مسیر کامل فایل را به آن بدهیمکد:io.input ([file])
تابع io.read
که باید بصورت زیر نوشته شود :کد:io.read (···)
و دقیق کار تابع کلون دارfile:read را میکند. یا باید در خط بالا ، قبل از علامت کلون ، از خروجی متغییر io.input که قبلا تعریف کرده بودیم ، استفاده کنیمکد:io.input():read
تابع io.output
در آرگومان آن ، بجای file از خروجی تابع io.open استفاده میکنیم و دقیق برعکس تابع io.input ، موقع کار با تابع نوشتن یعنی تابع io.write مورد استفاده قرار میگیردکد:io.output ([file])
تابع io.write
هر چه در آرگومان آن نوشته شود را در فایل مینویسد . اما باید به این صورت فراخوانی شود :کد:io.write (···)
یا قبل از عملگر کلون ،باید از متغییر استفاده شده در تابع io.output استفاده کنیمکد:io.output():write
تابع io.close
باعث بشته شدن فایل مورد نظر میشودکد:io.close ([file])
تابع io.lines
همان کار تابع file:lines را انجام میدهد.کد:io.lines ([filename])
بجای filename باید اسم فایل را نوشت
تابع io.type
یکی از توابع کاربردی که در قسمت توابع کلون دار وجود ندارد ، این تابع هست که اگر فایلی باز شده بود ، مقدار رشته ی "file" را برمیگرداند و اگر بسته بود مقدار رشته ی "closed file" را برمیگرداند و اگر ارور بدهد ، nil راکد:io.type(obj)
در آرگومان این تابع هم بجای obj ، مقدار خروجی تابع io.open را که همان هندل فایل مورد نظر بود را وارد کنید
مثال بدون توضیح :
1)
کد:-- Opens a file in read file = io.open("test.lua", "r") -- sets the default input file as test.lua io.input(file) -- prints the first line of the file print(io.read()) -- closes the open file io.close(file) -- Opens a file in append mode file = io.open("test.lua", "a") -- sets the default output file as test.lua io.output(file) -- appends a word test to the last line of the file io.write("-- End of the test.lua file") -- closes the open file io.close(file)
2)
کد:-- Opens a file in read mode file = io.open("test.lua", "r") -- prints the first line of the file print(file:read()) -- closes the opened file file:close() -- Opens a file in append mode file = io.open("test.lua", "a") -- appends a word test to the last line of the file file:write("--test") -- closes the open file file:close()






پاسخ با نقل قول
Bookmarks