본문 바로가기
Programming/Python

[ 고급 ] 파일명 관리 유틸리티 SR. (v.0.0.2 beta 001)

by The Programmer 2024. 10. 6.

1. Introduction

이 예제 프로그램은 파일명 관리 유틸리티, SR(Super Renamer)입니다. 코어 코드만 포함된 초기 시험판입니다.
 
이 프로그램은 중급/고급 대상 예제이므로 입문자들에게는 무리입니다. 지금은 전혀 Super스럽지(?) 않지만, 우선은 저에게도 필요한 프로그램입니다. ^.^;
 
파일명이 문자열로 되어 있어서 이 프로그램은 실용적인 문자열 처리 기법과 정규 표현식에 대한 종합 연습용 예제로 '가끔' 유용합니다. ^.^;
 
이 예제에 사용된 파일명 변경 규칙들은 계속 추가될 예정입니다. 아마도 프로그램 디자인(=설계) 방향이 규칙 함수들의 지속적인 추가-누적 방향으로 진행될 것으로 생각됩니다. 초기 코어 개발 기간 당분간은.

 
이후에는 아마도 GUI를 도입하고 규칙 함수들의 적용 및 제외 관리 쪽으로 주력하게 될 것으로 생각되고, Ai까지는 생각하고 있지 않지만 그렇게 될 수 있으면 좋겠습니다. Optional Rule-Based Renamer가 일단은 가시적인 개발 목표입니다.
 
GUI 부분은 처음에는 Tkinter로, 이후에는 wxPython이나 PyQt로 만들어 볼 생각입니다. 이 프로그램은 엄청난 기능보다는 프로그램의 사용성에 중점을 두고 개발됩니다.

 

[ SR. v.0.0.2 beta 001 ]

[ A real python, ㅡ.ㅡ;; Wuuooooaaaa...!]

 
 

"""

아래는 주어진 규칙에 따라 'C:\Temp' 폴더의 모든 파일 이름을 변경하는 파이썬 프로그램의 샘플 코드입니다. 여기서 지정된 폴더는 이후
동적 실행 환경 및 GUI를 통한 폴더 선택 방법 등을 통해 임의의 다른 폴더로 변경될 수 있습니다.

"""



2. Code

 

[ Public Python Logo Image ]

 
 
"""
SR. v.0.0.2 beta 001
Oct. 08th, 2024.
ITM.DG.James.

 
"""

 

import os
import re


# 폴더 경로 설정, 보통, GUI를 통해 동적으로 결정됨.
folder_path = r'C:\Temp'

 
def add_prefix_if_needed(sname):
        print("sname 1 : ", type(sname), " : ", sname)   
#check
        if re.match(r'^S5_', sname):   
# 문자열이 'S5_'로 시작하는지 확인, sname 타입 오류 주의.
                return sname
        elif not re.match(r'^S5_', sname): 
  
# 시작하지 않으면 'S5_'를 추가
                sname = 'S5_' + sname
                print("sname 2 : ", type(sname), " : ", sname)
#check
                return sname


# 파일 이름 변경 함수 정의
def rename_files_in_folder(folder_path):
        for filename in os.listdir(folder_path):

                # 파일의 전체 경로
                old_file_path = os.path.join(folder_path, filename)

                # 파일명에서 확장자 분리
                name, ext = os.path.splitext(filename)

               
 # 여기는 규칙 서술 부분 시작.
               
                # 이하 규칙 부분은 Regular Expression.

                # 규칙 1 : 모든 공백 문자를 밑줄(_)로 대체
                name = name.replace(' ', '_')

                # 규칙 2 : 모든 특수 기호를 밑줄(_)로 대체. 현재로서 이 규칙은 문제점이 많다.
                name = re.sub(r'[^A-Za-z0-9_]', '_', name)

                # 규칙 3 : 파일명이 숫자나 밑줄 문자로 시작하는 경우 'S5_' 추가
                if name[0].isdigit() or name.startswith('_'):
                         name = 'S5_' + name


               
# 새로운 규칙은 규칙 부분에 계속 추가될 수 있을 것.

                # 규칙 4 : 연속 반복되는 문자열 하나로 통합, 밑줄 문자의 경우임.
                name = re.sub(r'_{2,}', '_', name)

 
                # Rule 5 : 접두어 추가 혹은 스킵.

                name = add_prefix_if_needed(name)

                # 여기는 규칙 서술 부분 끝.

               
                # 새로운 파일명. 확장자 포함.
                new_filename = name + ext
               new_file_path = os.path.join(folder_path, new_filename)

                # 실제로 파일 이름을 변경하는 함수.
                os.rename(old_file_path, new_file_path)


                # 확인용 :

               print(f'Renamed: "{filename}" to "{new_filename}"')
                # 여기서 rename_files_in_folder() 함수 종료!

# 함수 호출
rename_files_in_folder(folder_path)


 

3. 코드 해설 Notes

 

약간의 코드 설명.
 

■ os 모듈 : 파일과 디렉토리 작업을 위한 모듈입니다....

■ re 모듈 : 정규 표현식을 사용하여 문자열을 조작합니다.

■ os.listdir(folder_path) : 지정된 폴더의 모든 파일 목록을 가져옵니다.

각 파일 이름에 대해 규칙을 적용하고 새 파일 이름으로 변경합니다. 순서는 유의미합니다.

os.rename(old_file_path, new_file_path) : 파일 이름을 변경합니다.

 
 
위 코드를 실행하기 전에 해당 폴더 경로가 올바른지 확인 필요함. 실무 환경에서의 폴더 경로 설정은 주로 동적으로 결정될 듯. 보통은 GUI를 통해서 특정 폴더 선택하는 방식으로!

 
 
  GUI + 누적 Rule Based 방식으로 구성 가능할 것.

 
 

4. 추가 사항들 Additions

 
□ 규칙 4, 파이썬에서 정규 표현식을 사용하여 문자열 중 연속된 밑줄 문자를 하나의 밑줄 문자로 줄이는 방법은 다음과 같습니다.

1) re 모듈 임포트 :
import re
 
2) 정규 표현식 사용 :
정규 표현식 r'_{2,}'를 사용하여 두 개 이상의 연속된 밑줄을 찾습니다.
 
3) re.sub() 함수를 사용하여 찾은 부분을 하나의 밑줄로 대체합니다.
 
규칙 4, 실제 구현 예제

import re

# 원본 문자열
text = "이___것은___예시___문자열입니다____."

# 연속된 밑줄을 하나로 줄이기
result = re.sub(r'_{2,}', '_', text)

print(result)

규칙 4, 코드 설명

r'_{2,}' : 두 개 이상의 밑줄을 찾는 정규 표현식입니다.
re.sub() : 첫 번째 인자는 패턴, 두 번째 인자는 대체할 문자열, 세 번째 인자는 원본 문자열입니다.
 
규칙 4, 실행 결과

위의 예제 코드를 실행하면, 출력 결과는 다음과 같습니다:

이_것은_예시_문자열입니다_.

이렇게 하면 연속된 여러 개의 밑줄이 하나의 밑줄로 줄어듭니다. 다른 문자열 중복의 경우에도 응용할 수 있는 유용한 코드입니다.

규칙 5, 문자열이  'S5_' 로 시작하는지 확인하고, 그렇지 않으면 문자열 맨 앞에  'S5_' 를 추가하는 방법은 다음과 같습니다.

 
1) re  모듈 임포트:
import re
 
2) 정규 표현식 사용:
정규 표현식  
^S5_ 를 사용하여 문자열이  S5_ 로 시작하는지 확인합니다.

3) 예제 코드

import re

def add_prefix_if_needed(sname):
        print("sname 1 : ", type(sname), " : ", sname)   
#check
        if re.match(r'^S5_', sname):   
        
# 문자열이 'S5_'로 시작하는지 확인
                return sname
        elif not re.match(r'^S5_', sname):
        
# 시작하지 않으면 'S5_'를 추가

                sname = 'S5_' + sname
                print("sname 2 : ", type(sname), " : ", sname)

                
#check
                return sname

# 예시 문자열
example1 = "example_string"
example2 = "S5_example_string"

# 결과 확인
print(add_prefix_if_needed(example1)
# 출력: S5_example_string
print(add_prefix_if_needed(example2)
# 출력: S5_example_string

4) 코드 설명
 ^S5_ : 문자열의 시작 부분에  S5_ 가 있는지를 확인하는 정규 표현식입니다.
 re.match() : 주어진 패턴이 문자열의 시작 부분과 일치하는지 검사합니다.

5) 실행 결과
위의 코드를 실행하면, 다음과 같은 결과를 얻을 수 있습니다:

 
S5_example_string
S5_example_string
 
이렇게 하면 주어진 문자열이  S5_ 로 시작하지 않을 경우, 문자열 앞에  S5_ 가 추가됩니다.

 
♨ 참고 : 어떤 경우에 다음과 같은 타입 오류가 일어나는지 주의해야 합니다.
 
Traceback (most recent call last):
  File "SR_v0_0_1_.py", line 77, in <module>
    rename_files_in_folder(folder_path)
  File "SR_v0_0_1_.py", line 63, in rename_files_in_folder
    new_filename = name + ext
TypeError: unsupported operand type(s) for +: 'NoneType' and 'str'

 


5. Updates

 
5.1. Updates #0001
 
Updates #0001은 SR 기본 코드와 분리된 독립적 프로그램 형태로 만들어 작동 테스트를 겸하고 있습니다. 이 업데이트 프로그램에서 충분히 테스트된 후 SR 기본 코드에 통합될 예정입니다.
 
Updates #0001은 tkinter GUI 대화상자를 통해서 특정 폴더를 선택하고 이 폴더의 이름을 Python 코드로 특정 변수에 저장하려고 합니다. 그리고 폴더 선택에 이 변수를 활용하려고 합니다. 이 코드들은 SR 프로그램의 실행 초기에 결정되는 특정 대상 폴더를 GUI 옵션을 통해 선택적으로 변경하는 역할을 합니다. 구체적으로는 아래 코드 부분을 GUI 옵션화합니다.

 
# 폴더 경로 설정, 보통, GUI를 통해 동적으로 결정됨.
folder_path = r'C:\Temp'

 
위 코드 부분을 Python tkinter 모듈의 GUI 대화상자를 통한 폴더 선택 방식으로 변경합니다.
 
■ 필요한 라이브러리
- tkinter: GUI를 생성하는 라이브러리
- tkinter.filedialog: 파일 대화상자를 열기 위해 필요

코드 예시

import tkinter as tk
from tkinter import filedialog

def select_folder():
    # Tkinter 윈도우 생성 (숨김).

    # 우선은, 윈도우 자체가 이 코드의 핵심이 아니기 때문임. 윈도우가 필요하기는 함. 필수 사항.
    root = tk.Tk()
    root.withdraw()    # 메인 윈도우 숨김

    # 폴더 선택 대화상자 열기
    folder_path = filedialog.askdirectory(title="폴더 선택") 

    # 선택한 폴더가 있을 경우
    if folder_path:
        print("선택한 폴더:", folder_path)
        return folder_path
    else:
        print("폴더를 선택하지 않았습니다.")

        # 폴더가 선택되지 않을 경우, 기본 폴더 설정을 사용하도록 이 부분에서 변경할 수도 있음.
        return None

# 함수 호출 및 선택한 폴더의 이름 저장
selected_folder = select_folder()

 
# 선택한 폴더의 이름을 출력, 그리고 if문 이외의 경우 처리.
if selected_folder:
    folder_name = selected_folder.split('/')[-1]    
# 폴더 경로에서 폴더 이름 추출
    print("폴더 이름:", folder_name)
else:
    selected_folder
= "D:\\"    # 대화상자를 통한 폴더 선택이 취소된 경우, 기본값으로 제공. 이하 동일.
    folder_name = ""   
# Default Value.
 
코드 설명

Tkinter 초기화 : tk.Tk()를 통해 Tkinter 객체를 생성하고, withdraw() 메서드를 사용하여 메인 윈도우를 숨깁니다.

cf. root.deiconify()
# 윈도우 보이기

 
폴더 선택: filedialog.askdirectory() 함수를 사용하여 폴더 선택 대화상자를 띄웁니다.
 
폴더 경로 처리 : 선택한 폴더의 경로에서 폴더 이름을 추출하기 위해 split('/')[-1]을 사용합니다.
이 부분은 운영 체제에 따라 경로 구분자가 다를 수 있으므로, 필요에 따라 조정할 수 있습니다.
이 부분에서 필요에 따라 '드라이브명 + 폴더 경로 전체'를 포함한 이름으로 대신할 수도 있을 것.
 
결과 출력: 선택한 폴더 경로와 이름을 출력합니다.
 
이 코드를 실행하면 폴더 선택 대화상자가 나타나며, 선택한 폴더의 경로와 이름이 변수에 저장됩니다.
 
 
보충 설명
 
위 코드 중에서 [-1] 부분 :
 
[-1] 부분은 파이썬에서 리스트나 문자열의 인덱스를 나타내는 방법 중 하나로, 리스트나 문자열의 '마지막 요소'를 선택하는 데 사용됩니다. 좀 더 자세히.

코드 설명

1. if selected_folder:
   - 이 조건문은 'selected_folder'가 존재하는지 확인합니다. 만약 'selected_folder'가 비어 있지 않다면, 다음 코드를 실행합니다.

2. folder_name = selected_folder.split('/')[-1]
   - selected_folder.split('/') :
   - selected_folder 문자열을 '/' 기호를 기준으로 나누어 리스트 형태로 만듭니다.
   - 예를 들어, selected_folder가 "D:/Documents/Projects"라면, split('/')의 결과는 ["D:", "Documents", "Projects"]입니다. 결과가 리스트 자료형입니다.

   - [-1] : 리스트의 마지막 요소를 선택합니다. 위의 예에서는 "Projects"가 됩니다.
   - 즉, folder_name에는 선택된 폴더의 '이름'이 할당됩니다.

3. print("폴더 이름:", folder_name)
   - 선택된 폴더 이름을 출력합니다.

4. else:
   - 만약 selected_folder가 비어 있다면(= 변수에 값이 할당되어 있지 않다면...의 뜻), 다음 코드를 실행합니다.

   - 'selected_folder가 비어 있다면'이라는 표현은 selected_folder 변수에 값이 할당되어 있지 않거나 값이 빈 문자열(empty string, "", 이중 인용 부호 사이에 공백이 없음, 문자열 자료형이라는 것만은 유지함)인 경우를 의미합니다. '선택된 폴더에 실제 파일들이 하나도 없다'라는 뜻이 아닙니다. 해석 주의.
  
'비어 있다'는 조건:
    - selected_folder가 'None'이거나
    - selected_folder가 빈 문자열("")인 경우. 주의 : 이중 인용 부호("") 사이에 공백(Space)이 없음.

예시

- 만약 selected_folder가 None이나 ""일 경우, if selected_folder:문이 False로 평가되므로 else: 블록이 실행됩니다.
- 따라서, selected_folder에 기본 경로인 "D:\\"가 할당되고, folder_name은 빈 문자열로 초기화됩니다.

이런 방식으로 selected_folder가 비어 있을 때의 처리를 하게 됩니다.

 
5. selected_folder = "D:\\"
   - 기본 경로를 "D:\"로 설정합니다.

6. folder_name = ""
   - 폴더 이름을 빈 문자열로 초기화합니다.

요약
- [-1]은 리스트의 '마지막 요소'를 선택하는 데 사용됩니다. 위의 코드에서는 선택된 폴더 경로의 마지막 부분, 즉 폴더 이름을 추출하는 데 사용됩니다.

 
리스트 기본기 필수 보충 : 리스트 카운트에 음수 인덱스 사용하기:

더보기

 

[ 리스트 기본기 보충 : 리스트 카운트에 음수 인덱스 사용하기 ]

 

리스트의 맨 뒤에 있는 최종 요소를 선택할 때 뒤에서부터 카운트하는 방법이 있으면 편리합니다. 리스트의 전체 크기를 몰라도 인덱스를 지정할 수 있으니 편리합니다.

 

다만 이때 주의할 점은 0부터 세지 않고 1부터 세며, 뒤에서부터 세기 때문에 앞에서부터 세는 경우와 차별점을 두어 양수가 아니라 음수를 사용합니다.

 

[ 앞에서부터 카운트 ]

리스트의 맨 앞 요소 : 0

앞에서 2번째 요소 : 1

앞에서 3번째 요소 : 2

......

......

 

[ 뒤에서부터 카운트 ]

리스트의 맨 뒤 요소 : -1

뒤에서 2번째 요소 : -2

뒤에서 3번째 요소 : -3

 

▶ 아마도, 추측이지만, 뒤에서부터 카운트를 할 때, 리스트의 맨 뒤 요소 인덱스 번호를 0으로 하면 0이 2개가 되는 사태(?)가 발생해서 이를 방지하기 위해서?? ^.^; +0과 -0이라는 개념은 없으니, 어쩌면. ^.^; 개인적인 추측입니다.

 

리스트 인덱싱에서 음수 인덱스를 사용하는 이유는 '리스트의 끝에서부터 요소를 쉽게 접근할 수 있도록 하기 위함'입니다. 음수 인덱스의 사용은 다음과 같은 장점이 있습니다.

▶ 음수 인덱스의 장점

1. 끝에서부터의 접근 용이성:
   - 음수 인덱스('-1', '-2', ...)는 리스트의 마지막 요소부터 시작하여 거꾸로 셀 수 있게 해줍니다. 
   - 예를 들어, 리스트의 마지막 요소를 쉽게 가져오고 싶을 때 '-1'을 사용하면 간단하고 직관적입니다.

2. 가독성:
   - 음수 인덱스를 사용하면 코드가 더 명확해질 수 있습니다. 예를 들어, 'some_list[-1]'는 "리스트의 마지막 요소"를 직접적으로 나타내므로, 코드의 의도가 분명해집니다.

3. 일관성:
   - 리스트의 길이에 관계없이 항상 마지막 요소를 참조할 수 있는 방법이 있습니다. 리스트의 길이가 변해도 음수 인덱스를 사용하면 항상 올바른 결과를 얻을 수 있습니다.

▶ 통일성에 대한 의견

- 음수 인덱스를 도입하지 않았다면, 리스트의 길이를 알아야 마지막 요소에 접근할 수 있는 방법을 찾아야 했을 것입니다. 예를 들어, 'some_list[len(some_list) - 1]'와 같이 작성해야 했습니다.
- 이렇게 되면 코드가 길어지고 가독성이 떨어질 수 있습니다.

▶ 결론

음수 인덱스는 리스트와 같은 자료구조에서 끝에서부터 요소에 접근하는 효과적인 방법이며, 이를 통해 코드의 간결함과 가독성을 높이는 데 기여합니다. 이러한 설계는 많은 프로그래밍 언어에서 채택하고 있는 유용한 패턴입니다.

 

^.^;

.

.

 

 
 
 
5.2 Updates # 0002
 
[ 콜백 함수의 선택적 실행법 ]

 
Updates #0002는 SR 기본 코드와 분리된 독립적 프로그램 형태로 만들어 작동 테스트를 겸하고 있습니다. 이 업데이트 프로그램에서 충분히 테스트된 후 SR 기본 코드에 통합될 예정입니다.
 
Updates #0002는 Tkinter GUI를 통한 콜백 함수를 선택적으로 실행하는 방법을 보여줍니다. 복수의 선택이 가능합니다.
 
import tkinter as tk

def function_one():
    print("Function One 실행!")

def function_two():
    print("Function Two 실행!")

def function_three():
    print("Function Three 실행!")

def run_selected_functions():
    if var1.get():
        function_one()
    if var2.get():
        function_two()
    if var3.get():
        function_three()

# 기본 윈도우 생성
root = tk.Tk()
root.title("함수 선택 실행기")

# 체크 버튼 변수
var1 = tk.BooleanVar()
var2 = tk.BooleanVar()
var3 = tk.BooleanVar()

# 체크 버튼 생성
check1 = tk.Checkbutton(root, text="Function One", variable=var1)
check2 = tk.Checkbutton(root, text="Function Two", variable=var2)
check3 = tk.Checkbutton(root, text="Function Three", variable=var3)

# 버튼 생성
run_button = tk.Button(root, text="선택된 함수 실행", command=run_selected_functions)

# 위젯 배치
check1.pack()
check2.pack()
check3.pack()
run_button.pack()

# 메인 루프 시작
root.mainloop()


간단한 코드 설명 :

함수 정의:  function_one ,  function_two ,  function_three 는 각각 실행할 함수입니다.

체크 버튼: 각 함수에 대해 체크 버튼을 생성하고, 선택 여부를  BooleanVar 로 관리합니다.
실행 버튼: "선택된 함수 실행" 버튼을 클릭하면 선택된 체크 버튼에 따라 함수가 실행됩니다.
메인 루프:  root.mainloop() 를 통해 GUI를 실행합니다.
 
이 코드를 실행하면 체크 박스를 통해 선택된 함수만 실행할 수 있습니다.
 
var1.get() 의 반환값은 불리언(Boolean) 자료형입니다.
 
var1 = tk.BooleanVar()
 
BooleanVar는 Tkinter에서 체크 버튼의 상태를 관리하는 변수로, 체크 버튼이 선택되면 True, 선택되지 않으면 False를 반환합니다. 따라서,  var1.get() 을 호출하면 해당 체크 버튼이 선택되었는지 여부를 나타내는 True 또는 False 값을 얻을 수 있습니다.
 

[ 콜백 함수의 선택적 실행법 예제 ]

 
 

6. 예정 사항 Things To Do


1) 폴더 문자열과 파일 문자열의 확장자 구분 문제. 마침표(.)가 문제가 될 때의 해법.
2) 폴더명이나 파일명에 이미 같은 문자열로 된 경우가 있을 경우의 해법. 중복의 문제.
3) Rule 5 함수 if... elif 관련 타입 오류 발생 원인 찾기.
4) 유니코드의 문제를 어떻게 할 것인가?
: S5_Español (España).spa.doc ==> S5_Espa_ol_Espa_a_spa.doc

: 대체-제거되어 버린다.
 
.

 

7. 첨부 파일 Files

 


GUI를 포함한 좀 더 긴 코드가 되면 별도 파일로 올려두겠습니다. 현재 GUI 관련 작업 중... ^.^;

.
.

8. 참고 자료 References

 
정규표현식Regular Expression 관련 교재들 좋아요. 출간된 지 제법 오래되었다고는 해도 가치가 있습니다. 여기서는 가장 얇은 교재로 한 권만.

1) 강환수, 신용현. 《파이썬으로 배우는 누구나 코딩》홍릉과학출판사. 2019.
2) 천인국. 《파워 유저를 위한 파이썬 Express》생능. 2020.
3) 벤 포터 저. 김경수 외 2인 공역《손에 잡히는 10분 정규표현식》인사이트. 2019.
4) Aria Ai.
5) chat-GPT Ai.

[ 벤 포터, 정규표현식, 한국어판 ]

    


Happy Programming!
^.^;


'Programming > Python' 카테고리의 다른 글

Tkinter 기본 연습 - 1 : 버튼과 pack() 사용법  (0) 2024.10.26
UniCode 연습 - 001  (0) 2024.10.09
[ L2P ] Python 입문 - Turtle 모듈 - 001  (0) 2024.09.15
Free IDE - Thonny  (0) 2024.09.08
turtle.pencolor() 함수  (0) 2023.12.10