راه
۶ کیو· متوسطمتوسطPYTHON۴ ثانیه · ۲۵۶ مگابایت

سازماندهی رسانه

داور پوشه‌ی مبدأ را خودش می‌سازد و مسیرها را در sys.argv می‌گذارد، پس کافی است مثل صورت سؤال sys.argv[1] و sys.argv[2] را بخوانی. بعد از اجرا، داور پوشه‌ی مقصد را فهرست می‌کند.

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

از شما می‌خواهیم یک اسکریپت پایتون به نام organize.py بنویسید که این وضعیت آشفته را سامان دهد. اسکریپت شما باید فایل‌های تصویری و ویدیویی را از یک پوشه‌ی مبدأ مشخص، بررسی کرده و آن‌ها را طبق قواعد زیر به پوشه‌ی مقصد کپی کند.

bash
نحوه‌ی اجرا از طریق خط فرمان:python3 organize.py <path_to_source_folder> <path_to_destination_folder>

💡 راهنمایی ۱

از ماژول sys و لیست sys.argv برای دریافت ورودی از طریق خط فرمان استفاده کنید.

python
import sys

inputs = sys.argv
source = inputs[1]
destination = inputs[2]

📋 معیارهای سازماندهی فایل‌ها

تعیین سال فایل

سال هر فایل بر اساس زمان آخرین تغییر (Last Modification Time) آن تعیین می‌شود.

برای دریافت زمان آخرین تغییر فایل، از تابع os.path.getmtime(file_path) استفاده کنید. هم‌چنین برای تبدیل timestamp به فرمت قابل‌خواندن و استخراج سال، از تابع time.ctime() استفاده کنید.

python
year = time.ctime(os.path.getmtime(file_path)).split()[-1]

تعیین نوع فایل

فایل‌ها باید بر اساس پسوندشان (به‌صورت غیر حساس به حروف بزرگ و کوچک) شناسایی شوند.

  • پسوندهای تصاویر: jpg، jpeg و png
  • پسوندهای ویدیوها: mp4، avi، 3gp، mpeg، mkv، wmv و mov

فایل‌هایی که پسوندشان در لیست‌های بالا وجود ندارد، باید نادیده گرفته شده و کپی نشوند. هم‌چنین فایل‌هایی که پسوند ندارند نباید کپی شوند.


📂 ساختار پوشه‌ی مقصد

در پوشه‌ی مقصد (<path_to_destination_folder>)، ساختار زیر باید ایجاد شود:

  1. برای هر سالی که حداقل یک عکس یا فیلم از آن سال در پوشه‌ی مبدأ یافت می‌شود، یک زیرپوشه با نام همان سال (مثلاً 2017 یا 2018) ایجاد کنید.
  2. درون هر پوشه‌ی سال، اگر تصویری برای آن سال وجود دارد، یک زیرپوشه به نام photos ایجاد کنید و تصاویر مربوط به آن سال را در این پوشه کپی کنید.
  3. درون هر پوشه‌ی سال، اگر ویدیویی برای آن سال وجود دارد، یک زیرپوشه به نام videos ایجاد کنید و ویدیوهای مربوط به آن سال را در این پوشه کپی کنید.
  • پوشه‌ها فقط در صورت نیاز باید ایجاد شوند. اگر برای سالی هیچ فایل عکس یا فیلمی یافت نشد، پوشه‌ی آن سال نباید ایجاد شود.
  • اگر در یک سال مشخص، مثلاً هیچ فیلمی وجود نداشت، پوشه videos برای آن سال نباید ایجاد گردد.
  • فایل‌های شناسایی‌شده باید از مکان اصلی خود در پوشه مبدأ به مکان محاسبه‌شده در پوشه مقصد کپی شوند. محتوای پوشه مبدأ نباید هیچ تغییری کند. (استفاده از os.rename مجاز نیست).
  • نام فایل‌ها در پوشه‌ی مقصد باید دقیقاً همان نام فایل‌ها در پوشه‌ی مبدأ باشد.
  • محتوای فایل‌ها در حین کپی نباید تغییر کند.
  • شما مجاز به استفاده از کتابخانه‌ی shutil نیستید. باید عملیات کپی فایل را به‌صورت دستی (خواندن از مبدأ و نوشتن در مقصد) پیاده‌سازی کنید.
  • از هیچ کتابخانه‌ی خارجی‌ای (که با pip install نصب می‌شود) استفاده نکنید. تنها از کتابخانه‌های استاندارد پایتون استفاده کنید.
  • استفاده از توابع os.system و os.popen برای اجرای دستورات سیستم‌عامل مجاز نیست.

🎯 مثال

فرض کنید ساختار پوشه‌ی مبدأ (src_folder) به شکل زیر است و تاریخ آخرین تغییر فایل‌ها همان تاریخی است که به‌صورت فرضی در کنار نام آن‌ها آمده‌است.

text
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) باید به شکل زیر باشد:

text
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 خالی خواهد بود.

فراموش نکنید که پسوند فایل‌ها باید به‌صورت غیرحساس به حروف کوچک و بزرگ مقایسه شوند.

python
import os

base_name, extension = os.path.splitext(file_path)
file_ext = extension.lstrip('.')

⚠️ فایل را چه‌طور کپی کنم؟

درباره‌ی نحوه‌ی کپی کردن فایل‌ها در انتهای درس‌نامه‌ی کار با فایل به‌صورت باینری خواندیم. برای کپی کردن فایل باید آن فایل را باز کنید، محتوای آن را بخوانید، سپس فایل مقصد را نیز باز کنید و محتوای خوانده‌شده را در آن بنویسید.

⚠️ کد روی سیستم خودم به‌درستی کار می‌کند؛ چرا این‌جا نمره نمی‌گیرم؟

برای ساختن مسیرهای فایل و پوشه (مانند dst_folder/2018/photos/image.jpg) حتماً از os.path.join() استفاده کنید تا اسکریپت شما هم در ویندوز و هم در لینوکس/مک به‌درستی کار کند. از الحاق رشته با / یا \\ به‌صورت دستی اکیداً خودداری کنید.


💡 راهنمایی نهایی

ساختار کلی اسکریپت به این شکل است:

python
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.join
  • os.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

برای نوشتن پاسخ و دریافت امتیاز، ابتدا وارد حساب شوید.

ورود