خواندن فایل و پردازش خطوط درون فایل چه سودی دارد؟
در زبانهای برنامهنویسی، راهکارهایی وجود دارد که برای انجام فعالیتهای تکراری و عادی استفاده میشوند و به بیان دقیقتر اجازه میدهند برخی فرآیندهای تکراری به شکل خودکار انجام شوند. بهطور مثال، خواندن یک فایل، ساخت حلقه و انجام فعالیتهای تکراری از طریق مجموعهای از دادهها و تغییر و دستکاری مقادیر تخصیص داده شده به متغیرها از وظایف اصلی برنامهنویسی است که برنامهنویسان با آن آشنا هستند. در دنیای برنامهنویسی برای انجام کارهای مختلف، روشهای مختلفی وجود دارد که برنامهنویسان برای بهینه کردن کدنویسیهای خود متناسب با شرایط بهترین گزینه را انتخاب میکنند. همانگونه که اشاره شد، شاید ورودی اسکریپتی که دریافت میکنید یک فایل متنی است که در هر یک از خطوط آن دادهها و اطلاعاتی قرار گرفته است. در این حالت در اسکریپت و برنامهای که مینویسید، دادههای درون هر خط فایل پردازش میشوند و خروجی مربوطه را تولید میکنند. در انتها میتوان خروجی را در فایل متنی دیگری ذخیرهساز، آنرا روی صفحهنمایش نشان داد یا کارهایی از این دست را انجام داد.
برخی از فایلهای متنی لینوکس، فایلهای پیکربندی هستند، در نتیجه هرگونه تغییری در این خصوص ممکن است تنظیماتی را تغییر داده یا مشکلاتی را به وجود آورند. به همین دلیل در برنامهنویسی و نوشتن اسکریپتهای سادهای که قرار است یکسری فعالیتهای روزانه را انجام دهند، در زمان تغییر تنظیمات سیستمی، خوان درست این فایلهای متنی اهمیت زیادی دارد. بنابراین لازم است تا دستورات مربوطه را بشناسید و در صورت نبود برنامههای ویرایش فایل متنی یا محدودیتهای مختلف، قادر باشید از طریق ترمینال لینوکس و خط فرمان از دستورات استفاده کنید.
برای خواندن خطوط درون یک فایل متنی چه دستوراتی در دسترس هستند؟
در Bash کاربران قادر هستند از While برای ساخت حلقه و انجام یکسری کارهای تکراری استفاده کنند. حلقه While به شما کمک میکند خطوط درون یک فایل متنی را تک به تک بخواهنید و پس از خواندن هر خط، کار مدنظر خود را انجام دهید. بهطور مثال، فرض کنید که یک فایل متنی ساده داریم که در آن چند خط حاوی نامهای سال میلادی به شرح زیر وجود دارد:
January
February
March
.
.
.
October
November
December
با استفاده از فرمان read این امکان فراهم است که خطوط مذکور را خوانده و از طریق فرمان echo اطلاعاتی که خوانده شده را در خطوط مجزا چاپ کنیم. دستور زیر فرآیند انجام چنین کاری را نشان میدهد.
while read line; do echo $line; done < data.txt
خروجی دستور فوق به شرح زیر است:
هنگامی که دستور read به انتهای خط میرسد و دیگری چیزی برای خواندن وجود ندارد، اجرای حلقه پایان میپذیرد. در برخی از زبانهای برنامهنویسی، فرآیند خواندن فایلهای متنی چندان ساده نیست و برنامهنویس در اولین گام باید فایل را باز کند و در ادامه دادههای درون فایل را بخواند، اما در رابط خط فرمان لینوکس، فرآیند فوق با سهولت انجام شده و در حقیقت Bash فرآیند هدایت حلقه به فایل متنی را مدیریت میکند.
لازم به توضیح است که دستور مفید دیگری بهنام cat نیز برای خواندن فایلهای متنی در دسترس کاربران قرار دارد که فرآیند فوق را سادهتر میکند.
اجازه دهید به سراغ مثال دیگری رویم. تصور کنید که فایل متنی شامل نام ماههای سال است، اما ماهها با کاراکتر \n که برای رفتن به خط جدیدی استفاده میشود، از یکدیگر متمایز شدهاند. بهطوری که محتوای فایل متنی به شرح زیر است:
January\n February\n March\n . . October\n November\n December\n
حال اگر در نظر داشته باشیم از ترکیب نحوی قبلی برای خواندن محتوای این فایل استفاده کنیم چه اتفاقی میافتد؟
while read line; do echo $line; done < data2.txt
همانگونه که در تصویر زیر مشاهده میکنید کاراکتر \ نادیده گرفته میشود و خروجی همانند حالت قبل است، با این تفاوت که اکنون یک کاراکتر n به انتهای نامهای میلادی افزوده شده است.
اگر در نظر داشته باشید کاراکتر \ حذف نشود، لازم است تغییراتی در دستورات پردازش فایل متنی اعمال کنید. بنابراین ایده بدی نیست که برای کار با فایلهای متنی اسکریپتهای سادهای را بنویسید.
چگونه خطوط یک فایل متنی را با اسکریپتنویسی بخوانیم؟
در لینوکس فرمت فایلی فایلهای اسکریپیتی که شامل دستوراتی هستند، sh است. بهطور مثال، تصور کنید که اسکریپتی بهنام script1.sh ایجاد کردهایم که محتوای درون آن به شرح زیر است:
#!/bin/bash
Counter=0
while IFS='' read -r LinefromFile || [[ -n "${LinefromFile}" ]]; do
((Counter++))
echo "Accessing line $Counter: ${LinefromFile}"
done < "$1"
در اسکریپت فوق، حلقه سادهای داریم که در آن متغیری به نام Counter که شمارنده حلقه است را تعریف کرده و مقدار اولیه آن را برابر با صفر در نظر گرفتهایم. هر زمان حلقه تکرار میشود، یک واحد به Counter اضافه میشود. اولین دستور در حلقه ایجاد شده با while با مقدار IFS تنظیم شده است. IFS یا به عبارت دقیقتر Internal Field Separator رشتهای است که Bash برای شناسایی محدوده لغتنامهها استفاده میکند. بهشکل پیشفرض دستور read فاصلهای ابتدا و انتها را حذف میکند، بنابراین اگر در نظر داشته باشید خطوط فایل را بدون حذف شدن فاصلههای ابتدا و انتها بخوانید، لازم است مقدار IFS را برابر با یک رشته خالی در نظر بگیرید، درست به همان شکلی که در ابتدای حلقه while اینکار انجام شده است. با اینحال، این امکان فراهم است که قبل از حلقه هم به IFS مقدار خالی تخصیص دهید، اما در اسکریپتنویسیهای پیچیده، بهتر است مقداردهی به IFS در حلقههای داخلی انجام شود تا نتیجه عجیبی دریافت نشود.
در مرحله بعد در حلقه، فرآیند خواندن یک خط از فایل متنی و قرار دادن آن در متغیری بهنام LinefromFile انجام میشود. برای آنکه کاراکتر \ نادیده گرفته نشود از گزینه -r استفاده شده تا کاراکتر \ همانند کاراکترهای عادی خوانده شود.
در ادامه در حلقه دو شرط قرار گرفته که در صورت برقرار بودن، پردازش در حلقه ادامه پیدا میکند.
حالت اول برقرار بودن شرط read -r LinefromFile است که توصیفکننده این موضوع است که در صورت موفقیتآمیز بودن خواندن خط جدید، سیگنال موفقیت به While ارسال شود و فرآیندی که در حلقه تعریف شده انجام شود. دقت کنید که دستور read زمانی موفق است که در انتهای خط، کاراکتر جدیدی را مشاهده کند. اگر فایل متنی با POSIX هماهنگ نباشد، شاید در انتهای آخرین خط فایل، کاراکتر تولیدکننده خط جدید در دسترس نباشد و به جای آن کاراکتر پایاندهنده فایل EOF قرار گرفته باشد. در نتیجه دستور read با شکست روبرو میشود و آخرین خط فایل در حلقه while را پردازش نمیکند، به همین دلیل لازم است که یکی از دو شرط برای اجرای حلقه برقرار باشند.
شرط دوم به این صورت است که فایل متنی پردازش شود و اگر د رانتهای آخرین خط، کاراکتر ساخت خط جدید موجود نبود، سیگنال موفقیت به while ارسال شود. در این حالت، زمانی که به انتهای خط میرسیم، بازهم خواندن خطوط ادامه پیدا میکند و حلقه اجرا میشود.
نکته دیگری که باید به آن دقت کنید این است که از اپراتور منطقی || که معادل OR است استفاده شده است. برای تعیین فایل متنی در اسکریپت فوق نیز از $1 استفاده شده که متغیری است که برای اجرای اسکریپت استفاده میشود. شما میتوانید اسکریپت فوق را درون برنامههای ویرایش متن ساده کپی کنید و آنرا با پسوند sh در مکان مربوطه ذخیره کنید. برای آنکه بتوانید فایل فوق را اجرا کنید باید از فرمان chmod به صورت زیر استفاده کنید:
chmod +x script1.sh
ارگ فایل متنی data2.txt نام دارد و در نظر داشته باشید با اسکریپت موردنظر آنرا پردازش کنید لازم است نام کامل فایل متنی را به عنوان اولین متغیر اسکریپت به آن تخصیص دهید. به همین دلیل به دستور زیر نیاز دارید:
./script1.sh data2.txt
خروجی دستور فوق به شرح زیر است:
همانگونه که مشاهده میکنید تمامی کاراکترهای درون فایل متنی به درستی خوانده شده و روی صفحهنمایش چاپ شدهاند.
چگونه خطوط فایل متنی را به تابعی در اسکریپت ارسال کنیم؟
تصور کنید که تابعی دارید که قرار است هر یک از خطوط فایل متنی را به عنوان ورودی دریافت کرده و در ادامه از خروجی تابع استفاده کنید. برای روشن شدن موضوع اسکریپت زیر را در نظر بگیرید:
#!/bin/bash
Counter=0
function process_line() {
echo "Processing line $Counter: $1"
}
while IFS='' read -r LinefromFile || [[ -n "${LinefromFile}" ]]; do
((Counter++))
process_line "$LinefromFile"
done < "$1"
در اسکریپت مذکور، قبل از ساخت حلقه، مقدار صفر به شمارنده تخصیص داده شده و علاوه بر این تابعی بهنام process_line() نیز تعریف شده است. دقت کنید که نمیتواند پس از حلقه، تابع را تعریف کنید و لازم است قبل از فراخوانی تابع، آنرا تعریف کرده باشید. علاوه بر این به نکته مهم دیگری نیز دقت کنید که قرار است هر خط از فایل متنی که خوانده میشود به عنوان ورودی به تابع تخصیص داده شود. دسترسی به متغیر فوق از طریق $1 انجام میشود. اگر دو متغیر داشته باشید و بهطور مثال در نظر داشته باشید خط بعدی به عنوان دومین متغیر به تابع ارسال شود، باید از $2 استفاده کنید و اینکار را برای متغیرهای بعدی نیز تکرار کنید.
در حلقه while خبری از دستور echo نیست، زیرا فرآیند چاپ در تابع process_line() تعریف شده است. بنابراین با فراخوان این تابع عمل چاپ نیز انجام میشود. در زمان فراخوان توابع در زمان اسکریپتنویسی لازم است به سه نکته مهم دقت کنید:
در فراخوانی تابع نیازی نیست از () برای فراخوانی استافده کنید. اما در تعریف تابع باید از پرانتز باز و بسته استفاده شود.
متغیرهایی که برای توابع ارسال میشوند باید میان کوتیشن قرار بگیرند تا اگر فاصلهای وجود داشت، اولین کلمه به عنوان متغیر اول و دومین کلمه به عنوان متغیر دوم به همنی شکل سایر کلمات به عنوان متغیرهای بعدی به تابع ارسال نشوند. بنابراین باید از ‘$1’ به جای $1 استفاده کنید.
به دلیل اینکه متغیر Counter بیرون از تابع تعریف شده و متغیری عمومی است، بهتر است داخل توابع از آن استفاده کنید و مقدار آنرا تغییر دهید. اگر در خود تابع Counter را تعریف کنید، هر مرتبه با یک متغیر جدید در ارتباط خواهید بود. در نهایت اسکریپت را ذخیره کنید و از طریق دستور chmod آنرا به یک فایل اجرایی تبدیل کنید.
chmod +x script2.sh
اکنون میتوانیم فایل متنی را به اسکریپت جدید تخصیص دهیم و خروجی را ارزیابی کنیم. این مرتبه فایل متنی شامل عبارت زیر است:
January
February
March
.
.
October
November \nMore text "at the end of the line"
December
دستور فراخوانی اسکریپت و تخصیص فایل به عنوان ورودی به شرح زیر است:
./script2.sh data3.txt
خروجی این دستور در تصویر زیر نشان داده شده است:
ماهنامه شبکه را از کجا تهیه کنیم؟
ماهنامه شبکه را میتوانید از کتابخانههای عمومی سراسر کشور و نیز از دکههای روزنامهفروشی تهیه نمائید.
ثبت اشتراک نسخه کاغذی ماهنامه شبکه
ثبت اشتراک نسخه آنلاین
کتاب الکترونیک +Network راهنمای شبکهها
- برای دانلود تنها کتاب کامل ترجمه فارسی +Network اینجا کلیک کنید.
کتاب الکترونیک دوره مقدماتی آموزش پایتون
- اگر قصد یادگیری برنامهنویسی را دارید ولی هیچ پیشزمینهای ندارید اینجا کلیک کنید.
نظر شما چیست؟