سازماندهی رسانه
داور پوشهی مبدأ را خودش میسازد و مسیرها را در
sys.argvمیگذارد، پس کافی است مثل صورت سؤالsys.argv[1]وsys.argv[2]را بخوانی. بعد از اجرا، داور پوشهی مقصد را فهرست میکند.
تصور کنید پوشهای روی کامپیوتر خود دارید که در طول زمان، عکسها و فیلمهای مختلفی را بدون نظم خاصی به آن منتقل کردهاید. اکنون این پوشه بسیار نامرتب است، فایلها دستهبندی مشخصی ندارند، ساختار پوشهبندی مناسبی وجود ندارد و حتی فایلهایی غیر از عکس و فیلم نیز در میان آنها یافت میشود.
از شما میخواهیم یک اسکریپت پایتون به نام organize.py بنویسید که این وضعیت آشفته را سامان دهد. اسکریپت شما باید فایلهای تصویری و ویدیویی را از یک پوشهی مبدأ مشخص، بررسی کرده و آنها را طبق قواعد زیر به پوشهی مقصد کپی کند.
نحوهی اجرا از طریق خط فرمان:python3 organize.py <path_to_source_folder> <path_to_destination_folder>
💡 راهنمایی ۱
از ماژول sys و لیست sys.argv برای دریافت ورودی از طریق خط فرمان استفاده کنید.
import sys
inputs = sys.argv
source = inputs[1]
destination = inputs[2]
📋 معیارهای سازماندهی فایلها
تعیین سال فایل
سال هر فایل بر اساس زمان آخرین تغییر (Last Modification Time) آن تعیین میشود.
برای دریافت زمان آخرین تغییر فایل، از تابع
os.path.getmtime(file_path)استفاده کنید. همچنین برای تبدیل timestamp به فرمت قابلخواندن و استخراج سال، از تابعtime.ctime()استفاده کنید.
year = time.ctime(os.path.getmtime(file_path)).split()[-1]
تعیین نوع فایل
فایلها باید بر اساس پسوندشان (بهصورت غیر حساس به حروف بزرگ و کوچک) شناسایی شوند.
- پسوندهای تصاویر:
jpg،jpegوpng - پسوندهای ویدیوها:
mp4،avi،3gp،mpeg،mkv،wmvوmov
فایلهایی که پسوندشان در لیستهای بالا وجود ندارد، باید نادیده گرفته شده و کپی نشوند. همچنین فایلهایی که پسوند ندارند نباید کپی شوند.
📂 ساختار پوشهی مقصد
در پوشهی مقصد (<path_to_destination_folder>)، ساختار زیر باید ایجاد شود:
- برای هر سالی که حداقل یک عکس یا فیلم از آن سال در پوشهی مبدأ یافت میشود، یک زیرپوشه با نام همان سال (مثلاً
2017یا2018) ایجاد کنید. - درون هر پوشهی سال، اگر تصویری برای آن سال وجود دارد، یک زیرپوشه به نام
photosایجاد کنید و تصاویر مربوط به آن سال را در این پوشه کپی کنید. - درون هر پوشهی سال، اگر ویدیویی برای آن سال وجود دارد، یک زیرپوشه به نام
videosایجاد کنید و ویدیوهای مربوط به آن سال را در این پوشه کپی کنید.
- پوشهها فقط در صورت نیاز باید ایجاد شوند. اگر برای سالی هیچ فایل عکس یا فیلمی یافت نشد، پوشهی آن سال نباید ایجاد شود.
- اگر در یک سال مشخص، مثلاً هیچ فیلمی وجود نداشت، پوشه
videosبرای آن سال نباید ایجاد گردد.- فایلهای شناساییشده باید از مکان اصلی خود در پوشه مبدأ به مکان محاسبهشده در پوشه مقصد کپی شوند. محتوای پوشه مبدأ نباید هیچ تغییری کند. (استفاده از
os.renameمجاز نیست).- نام فایلها در پوشهی مقصد باید دقیقاً همان نام فایلها در پوشهی مبدأ باشد.
- محتوای فایلها در حین کپی نباید تغییر کند.
- شما مجاز به استفاده از کتابخانهی
shutilنیستید. باید عملیات کپی فایل را بهصورت دستی (خواندن از مبدأ و نوشتن در مقصد) پیادهسازی کنید.- از هیچ کتابخانهی خارجیای (که با
pip installنصب میشود) استفاده نکنید. تنها از کتابخانههای استاندارد پایتون استفاده کنید.- استفاده از توابع
os.systemوos.popenبرای اجرای دستورات سیستمعامل مجاز نیست.
🎯 مثال
فرض کنید ساختار پوشهی مبدأ (src_folder) به شکل زیر است و تاریخ آخرین تغییر فایلها همان تاریخی است که بهصورت فرضی در کنار نام آنها آمدهاست.
src_folder/
├── IMG_2235.jpg # [modified time: 2018/01/28]
├── travel_photos/
│ ├── 2018-11-09_11-27-14.3gp # [modified time: 2018/11/09]
│ ├── IMG_20171017_052418.jpg # [modified time: 2017/10/17]
│ ├── 20180311_214539.JPG # [modified time: 2018/03/11]
│ ├── IMG_2237.jpg # [modified time: 2018/02/21]
│ └── note.txt # [ignored]
└── vid1/
├── images/
│ └── IMG_2014-01-12.JPG # [modified time: 2014/01/12]
└── VID_20170425_184731.mp4 # [modified time: 2017/04/25]
پس از اجرای دستور python3 organize.py src_folder dst_folder، ساختار پوشهی مقصد (dst_folder) باید به شکل زیر باشد:
dst_folder/
├── 2014/
│ └── photos/
│ └── IMG_2014-01-12.JPG
├── 2017/
│ ├── photos/
│ │ └── IMG_20171017_052418.jpg
│ └── videos/
│ └── VID_20170425_184731.mp4
└── 2018/
├── photos/
│ ├── 20180311_214539.JPG
│ ├── IMG_2235.jpg
│ └── IMG_2237.jpg
└── videos/
└── 2018-11-09_11-27-14.3gp
💡 چالشهای حل تمرین
⚠️ چگونه پوشهی مبدأ را پیمایش کنم و بین پوشهها جابهجا شوم؟
اکیداً توصیه میشود از
os.chdir()استفاده نکنید. جابهجا کردن اشارهگر مفسر میتواند پیچیدگی برنامه و احتمال خطا را بالا ببرد. در عمل، نیازی به جابهجایی اشارهگر نیست.با استفاده از
os.walk()میتوانید در پوشهی مبدأ پیمایش کنید. برای دسترسی به فایل مبدأ (برای بررسی تاریخ و سپس کپی کردن آن)، میتوانید آدرس کامل آن را توسط تابعos.path.joinتولید کنید. برای ساخت فایل مقصد نیز میتوانید بهطور مشابه، آدرس آن را با استفاده ازos.path.joinبسازید.
⚠️ دقیقاً چه پوشههایی را باید بسازم؟
وجود هرکدام از پوشههای مقصد باید پیش از ساخت، بررسی شود. حتی خود پوشهی مقصد که در ورودی به برنامه داده میشود نیز ممکن است وجود نداشته باشد. بنابراین هریک از پوشههای فیلم، عکس، سال و حتی پوشهی اصلی مقصد را باید بررسی کنید و در صورت عدم وجود، آنها را بسازید.
⚠️ پسوند فایل را چهطور استخراج کنم؟
همانطور که در تمرین قبل دیدید، برای استخراج پسوند فایل باید وجود
.قبل از پسوند را نیز حتماً در نظر داشته باشید. برای مثال، فایلی به نامqMkVیک فایل فیلم محسوب نمیشود.یکی دیگر از روشهای استخراج پسوند فایل، استفاده از تابع
os.path.splitextاست:این تابع، پسوند را به همراه
.برمیگرداند، بنابراین پیش از مقایسه با پسوندهای عکس و فیلم، باید.را از ابتدای آن برداریم. اگر فایل پسوندی نداشته باشد، متغیر extension خالی خواهد بود.فراموش نکنید که پسوند فایلها باید بهصورت غیرحساس به حروف کوچک و بزرگ مقایسه شوند.
import os
base_name, extension = os.path.splitext(file_path)
file_ext = extension.lstrip('.')
⚠️ فایل را چهطور کپی کنم؟
دربارهی نحوهی کپی کردن فایلها در انتهای درسنامهی کار با فایل بهصورت باینری خواندیم. برای کپی کردن فایل باید آن فایل را باز کنید، محتوای آن را بخوانید، سپس فایل مقصد را نیز باز کنید و محتوای خواندهشده را در آن بنویسید.
⚠️ کد روی سیستم خودم بهدرستی کار میکند؛ چرا اینجا نمره نمیگیرم؟
برای ساختن مسیرهای فایل و پوشه (مانند
dst_folder/2018/photos/image.jpg) حتماً ازos.path.join()استفاده کنید تا اسکریپت شما هم در ویندوز و هم در لینوکس/مک بهدرستی کار کند. از الحاق رشته با/یا\\بهصورت دستی اکیداً خودداری کنید.
💡 راهنمایی نهایی
ساختار کلی اسکریپت به این شکل است:
import os, time, sys
image_extensions = {'.jpg', '.jpeg', '.png'}
video_extensions = {'.mp4', '.avi', '.3gp', '.mpeg', '.mkv', '.wmv', '.mov'}
def copy_file(src, dst):
with open(src, 'rb') as src_file:
data = src_file.read()
with open(dst, 'wb') as dst_file:
dst_file.write(data)
def get_year(file_path):
return time.ctime(os.path.getmtime(file_path)).split()[-1]
def ensure_dir(path):
if not os.path.exists(path):
os.makedirs(path)
if __name__ == '__main__':
source = sys.argv[1]
destination = sys.argv[2]
for root, dirs, files in os.walk(source):
for file in files:
file_path = os.path.join(root, file)
year = get_year(file_path)
ext = os.path.splitext(file)[1].lower()
if ext in image_extensions:
target_dir = os.path.join(destination, year, 'photos')
ensure_dir(target_dir)
target_path = os.path.join(target_dir, file)
copy_file(file_path, target_path)
elif ext in video_extensions:
target_dir = os.path.join(destination, year, 'videos')
ensure_dir(target_dir)
target_path = os.path.join(target_dir, file)
copy_file(file_path, target_path)
sys.argv← دریافت مسیر مبدأ و مقصد از خط فرمانos.walk(source)← پیمایش تمام پوشهها و فایلهای مبدأos.path.splitext(file)[1].lower()← استخراج پسوند فایل (با حذف نقطه)time.ctime(os.path.getmtime(file_path)).split()[-1]← استخراج سال از زمان آخرین تغییرos.path.join(destination, year, 'photos')← ساخت مسیر مقصد باos.path.joinos.makedirs(target_dir)← ایجاد پوشهها در صورت نیاز- کپی فایل با
open()در حالت باینری ('rb'و'wb')
✅ این تمرین را در ویرایشگر خود پیادهسازی کنید. به یاد داشته باشید که اسکریپت باید از طریق خط فرمان اجرا شود و از کتابخانهی
shutilاستفاده نکند.
نمونهها
src_folder/IMG_2235.jpg 2018-01-28 src_folder/travel_photos/2018-11-09_11-27-14.3gp 2018-11-09 src_folder/travel_photos/IMG_20171017_052418.jpg 2017-10-17 src_folder/travel_photos/20180311_214539.JPG 2018-03-11 src_folder/travel_photos/IMG_2237.jpg 2018-02-21 src_folder/travel_photos/note.txt 2018-05-05 src_folder/vid1/images/IMG_2014-01-12.JPG 2014-01-12 src_folder/vid1/VID_20170425_184731.mp4 2017-04-25
2014/ 2014/photos/ 2014/photos/IMG_2014-01-12.JPG <- src_folder/vid1/images/IMG_2014-01-12.JPG 2017/ 2017/photos/ 2017/photos/IMG_20171017_052418.jpg <- src_folder/travel_photos/IMG_20171017_052418.jpg 2017/videos/ 2017/videos/VID_20170425_184731.mp4 <- src_folder/vid1/VID_20170425_184731.mp4 2018/ 2018/photos/ 2018/photos/20180311_214539.JPG <- src_folder/travel_photos/20180311_214539.JPG 2018/photos/IMG_2235.jpg <- src_folder/IMG_2235.jpg 2018/photos/IMG_2237.jpg <- src_folder/travel_photos/IMG_2237.jpg 2018/videos/ 2018/videos/2018-11-09_11-27-14.3gp <- src_folder/travel_photos/2018-11-09_11-27-14.3gp source files still in place: 8
src_folder/a.PNG 2020-06-01 src_folder/b.jpeg 2020-06-02 src_folder/clip.MoV 2021-01-01 src_folder/film.mkv 2021-02-02 src_folder/qMkV 2021-03-03 src_folder/old/x.wmv 2019-07-07 src_folder/old/y.mpeg 2019-08-08 src_folder/old/z.avi 2019-09-09 src_folder/old/w.3GP 2019-10-10 src_folder/README 2020-01-01
2019/ 2019/videos/ 2019/videos/w.3GP <- src_folder/old/w.3GP 2019/videos/x.wmv <- src_folder/old/x.wmv 2019/videos/y.mpeg <- src_folder/old/y.mpeg 2019/videos/z.avi <- src_folder/old/z.avi 2020/ 2020/photos/ 2020/photos/a.PNG <- src_folder/a.PNG 2020/photos/b.jpeg <- src_folder/b.jpeg 2021/ 2021/videos/ 2021/videos/clip.MoV <- src_folder/clip.MoV 2021/videos/film.mkv <- src_folder/film.mkv source files still in place: 10
src_folder/one/pic.jpg 2015-05-05 src_folder/two/pic.jpg 2016-06-06 src_folder/two/deeper/movie.mp4 2016-07-07
2015/ 2015/photos/ 2015/photos/pic.jpg <- src_folder/one/pic.jpg 2016/ 2016/photos/ 2016/photos/pic.jpg <- src_folder/two/pic.jpg 2016/videos/ 2016/videos/movie.mp4 <- src_folder/two/deeper/movie.mp4 source files still in place: 3
src_folder/notes.txt 2020-01-01 src_folder/data/table.csv 2021-01-01
source files still in place: 2
برای نوشتن پاسخ و دریافت امتیاز، ابتدا وارد حساب شوید.
ورود