oruji.github.io
oruji.github.ioPersian Tutorials
ویرایش: 1396/11/22 21:04
A A

آموزش فایل در پایتون

در این آموزش از کتاب برخط پایتون (Python) با مفاهیم فایل و دیتابیس و مفاهیم پایه ی دیگر آشنا خواهیم شد.

ماندگاری داده ها

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

ولی برنامه های دیگر به اصطلاح ماندگار هستند: این برنامه ها حداقل برخی از داده های خود را درون یک مخزنی (به عنوان مثال، هارد درایو) ذخیره می کنند؛ و در صورتی که برنامه خاموش یا دوباره راه اندازی شود، داده های خود را برای استفاده در اختیار خواهند داشت.

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

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

روش دیگر، ذخیره داده های یک برنامه درون یک دیتابیس یا پایگاه داده می باشد. در این فصل یک دیتابیس ساده و یک ماژول به نام pickle را برای ذخیره داده های یک برنامه نشان خواهیم داد.

خواندن و نوشتن درون فایل در پایتون

فایل متنی ترتیبی از کارکترهای ذخیره شده در یک هارد دائمی مانند هارد درایو، فلش یا CD-ROM می باشد. نحوه باز کردن و خواندن فایل را در فصل بازی با کلمات آشنا شدیم.

برای نوشتن بر روی فایل، باید از 'w' به عنوان پارامتر دوم در تابع open استفاده کرد:

>>> fout = open('output.txt', 'w') >>> print fout <open file 'output.txt', mode 'w' at 0xb7eb2410>

در صورتی که فایل مورد نظر وجود داشته باشد، باز کردن آن در حالت نوشتن ('w')، داده های قبلی را به طور کامل پاک کرده و یک فایل جدید را ایجاد می کند، بنابراین در این مورد باید کمی مواظب باشید! در صورتیکه فایل وجود نداشته باشد نیز یک فایل جدید ساخته می شود.

متد write داده ها را درون فایل قرار می دهد.

>>> line1 = "This here's the wattle,\n" >>> fout.write(line1)

شیء فایل جایی را که قرار دارد را درون خود نگه می دارد، بنابراین در صورتیکه متد write را دوباره فراخوانی کنید، داده مورد نظر دوباره به انتهای فایل اضافه می شود.

>>> line2 = "the emblem of our land.\n" >>> fout.write(line2)

هنگامی که نوشتن بر روی فایل خاتمه یافت، باید فایل را با استفاده از متد close ببندید.

>>> fout.close()

عملگر فرمت (%)

آرگومان write باید یک رشته باشد، بنابراین در صورتیکه بخواهیم مقادیر دیگری غیر ار رشته به متد write ارسال کنید، باید آن مقدار را به رشته تبدیل کنیم. ساده ترین راه استفاده از تابع str می باشد:

>>> x = 52 >>> f.write(str(x))

روش دیگر برای تبدیل مقادیر دیگر به رشته، استفاده از عملگر فرمت (%) می باشد. در صورتیکه از این عملگر برای یک مقدار integer استفاده شود، کارکرد آن به شکل عملگر modulus (باقی مانده تقسیم) خواهد بود. ولی هنگامی که برای یک مقدار رشته از این عملگر استفاده شود به صورت عملگر فرمت رفتار خواهد کرد.

اولی عملوند در این حالت فرمت رشته می باشد، که حاوی یک یا چندین ترتیب فرمت می باشد و تعیین می کند که عملوند دوم به چه صورت قالب بندی شود.

برای مثال، '%d' بدین معنی است که عملوند دوم باید یک integer باشد (d مخفف decimal است):

>>> camels = 42 >>> '%d' % camels '42'

نتیجه رشته '42' است، که هیچ تداخلی با 42 که یک integer است ندارد.

ترتیب فرمت می توانید در هر قسمتی از رشته قرار گیرد، بنابراین شما می توانید یک مقدار را درون یک جمله جای دهید:

>>> camels = 42 >>> 'I have spotted %d camels.' % camels 'I have spotted 42 camels.'

در صورتیکه بیشتر از یک فرمت درون رشته وجود داشته باشد، آرگومان دوم باید یک مقدار از نوع تاپل باشد. هر فرمت به ترتیب مطابق با یک المان از تاپل خواهد بود.

در مثال زیر از '%d' برای فرمت یک integer، از '%g' برای فرمت یک عدد اعشاری (نپرس چرا)، و از '%s' برای فرمت یک رشته استفاده شده است:

>>> 'In %d years I have spotted %g %s.' % (3, 0.1, 'camels') 'In 3 years I have spotted 0.1 camels.'

تعداد المان های درون تاپل باید با تعداد فرمت های استفاده شده درون رشته مطابق باشد. همچنین، نوع المان ها نیز باید با فرمت ها یکی باشد:

>>> '%d %d %d' % (1, 2) TypeError: not enough arguments for format string >>> '%d' % 'dollars' TypeError: illegal argument type for built-in operation

در خط اول کد فوق، تعداد المان ها کافی نیستند، و در خط سوم نوع المان یک نوع اشتباه می باشد.

عملگر فرمت بسیار قدرتمند می باشد، ولی استفاده از آن می تواند کمی مشکل باشد. برای اطلاعات بیشتر در مورد این عملگر می توانید به مستندات پایتون مراجعه کنید.

نام فایل و مسیر

فایل ها اصولا درون دایرکتوری ها (فولدر نیز نامیده می شود) قرار می گیرند. هر بنامه ای که در حال اجرا می باشد، دارای یک دایرکتوری جاری می باشد، که دایرکتوری پیشفرض برای اغلب عملیات ها می باشد. برای مثال، هنگامی که شما فایلی را برای خواندن باز می می کنید، پایتون درون دایرکتوری جاری به دنبال آن می گردد.

ماژول os دارای توابعی برای کار با فایل ها و دایرکتوری ها می باشد (os مخفف "operating system" است). os.getcwd نام دایرکتوری جاری را بر می گرداند:

>>> import os >>> cwd = os.getcwd() >>> print cwd /home/dinsdale

اصطلاح cwd مخفف "current working directory" به معنی دایرکتوری جاری در حال کار می باشد. نتیجه مثال فوق /home/aminpy می باشد، که دایرکتوری خانگی کاربری با نام aminpy می باشد (در سیستم عامل لینوکس).

شبه رشته (string like) فوق یعنی cwd یک فایل را که مسیر (path) نامیده می شود را شناسایی می کند. مسیر نسبی (relative path) از دایرکتوری جاری شروع می شود و مسیر کامل (absolute path) از بالاتری دایرکتوری سیستم فایل شروع می شود.

مسیرهایی که تا کنون دیدیم یک نام ها فایل ساده بوده اند، بنابراین این مسیرها مسیرهای نسبی از دایرکتوری جاری بوده اند. برای یافتن یک مسیر کامل به یک فایل، می توانیم از os.path.abspath استفاده کنیم:

>>> os.path.abspath('memo.txt') '/home/dinsdale/memo.txt'

تابع os.path.exists وجود فایل یا دایرکتوری را بررسی می کند:

>>> os.path.exists('memo.txt') True

در صورتیکه فایل مورد نظر وجود داشته باشد، os.path.isdir بررسی می کند که آن دایرکتوری است یا خیر:

>>> os.path.isdir('memo.txt') False >>> os.path.isdir('music') True

به همین ترتیب، os.path.isfile نیز بررسی می کند که آیا آرگومان ارسالی به آن یک فایل است یا خیر.

تابع os.listdir لیستی از فایل ها و دایرکتوری موجود در دایرکتوری داده شده را بر می گرداند:

>>> os.listdir(cwd) ['music', 'photos', 'memo.txt']

برای شرح این توابع، مثال زیر درون یک دایرکتوری گشته، و اسامی تمامی فایل ها را چاپ کرده، و خودش را به صورت بازگشتی (recursively) درون تمام دایرکتوری ها فراخوانی می کند.

def walk(dirname): for name in os.listdir(dirname): path = os.path.join(dirname, name) if os.path.isfile(path): print path else: walk(path)

تابع os.path.join نام یک دایرکتوری و فایل را دریافت کرده و آن ها را در یک مسیر کامل شده به هم متصل می کند.

کنترل خطا در فایل

هنگام خواندن یا نوشتن درون فایل اشتباهات بسیاری می تواند رخ دهد. در صورتیکه سعی کنید فایلی را که وجود ندارد را باز کنید، خطای IOErro رخ خواهد داد:

>>> fin = open('bad_file') IOError: [Errno 2] No such file or directory: 'bad_file'

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

>>> fout = open('/etc/passwd', 'w') IOError: [Errno 13] Permission denied: '/etc/passwd'

و در صورتیکه یک دایرکتوری را برای خواندن باز کنید، خطای زیر را خواهید دید:

>>> fin = open('/home') IOError: [Errno 21] Is a directory

برای جلوگیری از خطاهای فوق، می توان از توابعی مانند os.path.exists و os.path.isfile استفاده کرد، ولی این کار زمان زیادی را خواهد گرفت و کد تمام احتمالات را باید بررسی کنید (if “Errno 21” is any indication, there are at least 21 things that can go wrong).

بهتر است زمانی که مشکل رخ داد با آن درگیر شویم، این دقیقا کاری است که عبارت try انجام می دهد. syntax آن شبیه به عبارت if است:

try: fin = open('bad_file') for line in fin: print line fin.close() except: print 'Something went wrong.'

پایتون با اجرای بلاک try آغاز می کند. در صورتیکه همه چیز بدون مشکل در بلاک try انجام شود، پردازش بلاک except نادیده گرفته خواهد شد. در صورتی که یک خطا رخ دهد، مفسر از بلاک try بیرون پریده و بلاک except را اجرا می کند.

مدیریت خطا با یک جمله try را کنترل خطا می گویند. در مثال فوق، بلاک except یک پیام خطا چاپ می کند که کمک زیاد نمی کند. به طور کلی، کنترل یک خطا به شما شانس این را می دهد که مشکل را حل کرده، یا دوباره سعی کرده و یا حداقل برنامه را به آرامی خاتمه دهید.

دیتابیس ها

دیتابیس یک فایل است که برای ذخیره سازی داده سازمان دهی شده است. اغلب دیتابیس ها همانند دیکشنری ساخته شده اند، بدین معنی که کلیدها را به مقادیر مرتبط می کنند. بزرگترین فرق آن این است که دیتابیس بر روی دیسک (یا هاردهای دائمی دیگر) می باشد، بنابراین بعد از اتمام برنامه نیز باقی خواهد ماند.

ماژول anydbm یک رابط برای ایجاد و به روز رسانی فایل های دیتابیس فراهم می کند. به عنوان مثال، ما یک دیتابیس که حاوی فایل های تصویری و عناوین آن می باشد را ایجاد خواهیم کرد:

>>> import anydbm >>> db = anydbm.open('captions.db', 'c')

حالت 'c' بدین معنی است که دیتابیس در صورتی که وجود نداشته باشد ایجاد شود. نتیجه یک شیء دیتابیس است که می تواند (برای اغلب عملیات ها) مانند یک دیکشنری استفاده شود. در صورتیکه یک مورد جدید ایجاد کنیم، anydbm فایل دیتابیس را به روز رسانی می کند.

>>> db['cleese.png'] = 'Photo of John Cleese.'

هنگامی که به یک از موارد دسترسی پیدا می کنیم، anydbm فایل را می خواند:

>>> print db['cleese.png'] Photo of John Cleese.

در صورتیکه انتساب دیگری را برای یک کلید موجود ایجاد کنیم، anydbm مقدار جدید را با مقدار قدیمی جایگزین می کند:

>>> db['cleese.png'] = 'Photo of John Cleese doing a silly walk.' >>> print db['cleese.png'] Photo of John Cleese doing a silly walk.

بسیاری از متدهای دیکشنری، مانند keys و items، با شیء های دیتابیس کار می کنند. بنابراین عملیات تکرار را با یک جمله for انجام می دهیم.

for key in db: print key

همانند فایل های دیگر، باید با استفاده از متد close شیء دیتابیس را مسدود کنیم:

>>> db.close()

Pickling

یکی از محدودیت های anydbm این است که نوع کلید و مقدار استفاده شده در آن باید رشته باشد. در صورتیکه از انواع دیگر در آن استفاده کنید، با خطا مواجه خواهید شد.

ماژول pickle در این مورد سودمند خواهد بود. این ماژول تقریبا تمامی نوع شیء ها را به رشته مناسب با آن برای ذخیره درون دیتابیس ترجمه کرده و رشته های ترجمه شده را به شیء های قبلی آن بر می گرداند.

تابع pickle.dumps یک شیء را به عنوان پارامتر دریافت کرده و یک رشته متناظر با آن بر می گرداند (dumps کوتاه شده "dump string" می باشد):

>>> import pickle >>> t = [1, 2, 3] >>> pickle.dumps(t) '(lp0\nI1\naI2\naI3\na.'

فرمت رشته برگردانده شده واضح به نظر نخواهد رسید؛ این رشته جهت ساده شدن برای picke جهت تفسیر یه این شکل در آمده است. pickle.loads("loads string") شیء را بازسازی می کند:

>>> t1 = [1, 2, 3] >>> s = pickle.dumps(t1) >>> t2 = pickle.loads(s) >>> print t2 [1, 2, 3]

هرچند شیء جدید مقداری مشابه با شیء قدیمی دارد، ولی به طور کلی این دو شیء با یکدیگر یکی نخواهند بود:

>>> t1 == t2 True >>> t1 is t2 False

به عبارت دیگر، عمل pickling و سپس unpickling، تاثیری یکسان با کپی کردن شیء دارد.

می توان از pickle برای ذخیره داده هایی به غیر از رشته در دیتابیس استفاده کرد. در حقیقت، این ترکیبی بسیار رایج است که درون ماژولی به نام shelve قرار گرفته است.

Pipes

اغلب سیستم عامل ها دارای یک رابط خط فرمان شناخته شده به عنوان shell می باشند. shell ها معمولا دارای دستوراتی برای هدایت فایل سیستم و اجرای برنامه ها هستند. برای مثال در یونیکس می توان دایرکتوری ها را با دستور cd تغییر داد، می توان با دستور ls محتویات یک دایرکتوری را نمایش داد، و (به عنوان مثال) با تایپ دستور firefox مرورگر وب را اجرا کرد.

هر برنامه ای که را که از shell اجرا می کنید را، با استفاده از pipe می توان در پایتون نیز اجرا نمود. pipe یک شیء است که یک برنامه در حال اجرا را نشان می دهد.

به عنوان مثال، دستور یونیکس ls -l معمولا محتویات دایرکتوری فعلی (در یک فرمت بلند) نمایش می دهد. می توان دستور ls را با استفاده از os.popen (بهتر است از subprocess به جای آن استفاده کنید، ولی برای سادگی و درک مطلب در اینجا ما از این ماژول استفاده کردیم.) درون پایتون اجرا نمود.

>>> cmd = 'ls -l' >>> fp = os.popen(cmd)

آرگومان ارسالی یک رشته حاوی دستور shell می باشد. مقدار برگشتی یک شیء است که درست مثل یک فایل باز شده رفتار می کند. می توان خروجی پردازش ls را خط به خط با readline بدست آورد و یا با read به کل خروجی دسترسی پیدا کرد:

>>> res = fp.read()

بعد از اتمام کار، مانند فایل شیء مورد نظر را close می کنیم:

>>> stat = fp.close() >>> print stat None

مقدار برگشتی آخرین وضعیت پردازش ls می باشد، None به معنای پایان یافتن عادی (بدون خطا) می باشد.

برای مثال، اغلب سیستم های Unix دستوری با نام md5sum را برای خواندن محتویات یک فایل و محاسبه یک "checksum" ارائه می کنند. برای اطلاعات بیشتر در مورد MD5 می توانید به http://en.wikipedia.org/wiki/Md5 مراجعه کنند. این دستور یک روش موثر برای بررسی یکی بودن محتویات دو فایل ارائه می کند. The probability that different contents yield the same checksum is very small (that is, unlikely to happen before the universe collapses).

می توان از pipe برای اجرای md5sum در پایتون و گرفتن نتیجه آن استفاده کرد:

>>> filename = 'book.tex' >>> cmd = 'md5sum ' filename >>> fp = os.popen(cmd) >>> res = fp.read() >>> stat = fp.close() >>> print res 1e0033f0ed0656636de0d75144ba32e0 book.tex >>> print stat None

ماژول نویسی

هر فایلی که حاوی کد پایتون باشد می تواند به عنوان یک ماژول import شود. به عنوان مثال، فرض کنید فایلی به نام wc.py با محتویات زیر داریم:

def linecount(filename): count = 0 for line in open(filename): count = 1 return count print linecount('wc.py')

در صورتیکه برنامه فوق را اجرا کنیم، برنامه محتویات خود را خوانده و تعداد خطوط موجود در فایل را که عدد ۷ باشد را چاپ می کند. شما همچنین می توانید آن را import کنید:

>>> import wc 7

حالا ما یک شیء ماژول به نام wc در اختیار داریم:

>>> print wc <module 'wc' from 'wc.py'>

این ماژول دارای تابعی به نام linecount می باشد:

>>> wc.linecount('wc.py') 7

و همه این ها مراحل نوشتن یک ماژول در پایتون نشان می دهند.

تنا مشکل موجود در مثال فوق این است که، زمانی که ما ماژول را import می کنیم، کد آزمایشی پایین ماژول اجرا می شود. به طور معمول زمانی که یک ماژول را import می کنیم، این کار توابع جدید را تعریف می کند، ولی آن ها را اجرا نمی کند.

برنامه هایی که به صورت ماژول import می شوند، اغلب از کد زیر استفاده می کنند:

if __name__ == '__main__': print linecount('wc.py')

__name__ یک متغیر داخلی پایتون است که زمانی که برنامه شروع می شود مقدار دهی می گردد. در صورتیکه برنامه مانند یک script اجرا شود، __name__ دارای مقدار __main__ می باشد؛ در این مورد، کد آزمایشی اجرا می شود. در غیر اینصورت، در صورتیکه ماژول import شود، کد آزمایشی نادیده گرفته می شود.