import os import re import time import uuid import rarfile import zipfile import shutil import difflib import subprocess import hashlib from pathlib import Path m_dbgLevel = 0 listDbgStr: list[str] = [] # def IsEmptyStr(string: str) -> bool: temp = f"{string}" return 0 == len(temp.strip()) # def GetCurrentTime() -> str: # 현재 시간을 구하고 구조체로 변환 current_time_struct = time.localtime() # 구조체에서 연, 월, 일, 시간, 분, 초를 추출 year = current_time_struct.tm_year month = current_time_struct.tm_mon day = current_time_struct.tm_mday hour = current_time_struct.tm_hour minute = current_time_struct.tm_min second = current_time_struct.tm_sec strRet = (f"{year}-{month}-{day}_{hour}:{minute}:{second}") return strRet #for debug def DbgOut(strInput:str, bPrint:bool = False): strMsg = (f"{GetCurrentTime()} : {strInput}") listDbgStr.append(strMsg) if True == bPrint: print(strMsg) # def printDbgMessages(): for line in listDbgStr: print(line) # def SaveDbgMessages(Path: str): try: with open(Path, 'w') as file: for line in listDbgStr: file.write(line + "\n") except IOError: DbgOut(f"Error: Could not write to the file at {Path}.", True) # 입력된 경로의 자식 폴더를 찾아 반환한다. # 반환하는 리스트는 리커시브 - 손자, 증손자 폴더까지 전부 포함한다 def ListSubDirectories(root_dir:str)-> list[str]: subdirectories: list[str] = [] # root_dir에서 하위 디렉토리 및 파일 목록을 얻음 for dirpath, dirnames, filenames in os.walk(root_dir): # 하위 디렉토리 목록을 반복하며 하위 디렉토리만 추출 for dirname in dirnames: path = os.path.join(dirpath, dirname) if True == IsFinalFolder(path): subdirectories.append(path) return subdirectories # 자식 폴더를 구해온다. 직계 자식만 def ListChildDirectories(pathDir:str, bVisibleOnly: bool = True) -> list[str]: listRet:list[str] = [] listTemp = os.listdir(pathDir) for name in listTemp: pathChild = os.path.join(pathDir, name) if os.path.isdir(pathChild): if True == bVisibleOnly and name.startswith('.'): continue listRet.append(name) return listRet # PathDir 에 지정된 폴더의 파일목록만 구해온다. 자식 폴더에 있는건 무시. def ListContainFiles(pathDir:str, bVisibleOnly:bool = True)-> list[str]: listRet:list[str] = [] listTemp = os.listdir(pathDir) for name in listTemp: pathChild = os.path.join(pathDir, name) if os.path.isfile(pathChild): if True == bVisibleOnly and name.startswith('.'): continue listRet.append(name) return listRet # 리스트에 담긴 확장자와 같은 확장자를 가진 파일을 찾아서 리스트로 반환 def ListFileExtRcr(pathTrg: str, listExt: list[str]) -> list[str]: listRet:list[str] = [] # pathTrg의 하위 디렉토리 및 파일 목록을 얻음 for dirpath, dirnames, filenames in os.walk(pathTrg): for file in filenames: extTmp = GetExtStr(file) if extTmp.lower() in listExt and not file.startswith('.'): listRet.append(os.path.join(dirpath, file)) return listRet # 입력된 경로에서 부모 폴더를 찾는다. 0 은 자기자신 1은 부모, 숫자대로 위로. def GetParentDirName(FullPath : str, nUp : int)->str: parts = FullPath.split(os.sep) nTrgIdx = 0 if nUp < len(parts): nTrgIdx = len(parts) -nUp -1 elif nUp < 0: nTrgIdx = len(parts) - 1 else: nTrgIdx = 0 return parts[nTrgIdx] # 입력된 경로가 자식 폴더를 가지고 있는지 판단한다.- 최종 폴더인지 여부 # 자식이 없으면 True, 자식이 있으면 False def IsFinalFolder(path : str) -> bool: bRet = True contents = os.listdir(path) for item in contents: if True == os.path.isdir(item): bRet = False break return bRet; # 어떤 경로 안에서 특정 확장자의 파일을 뽑아내어 그 리스트를 반환한다. def FindFileFromExt(path: str, ext: str)-> list[str]: bDot = False if 0 <= ext.find('.'): bDot = True listRet:list[str] = [] if False == os.path.exists(path): return listRet contents = os.listdir(path) for item in contents: if True == os.path.isdir(item): continue extItem = GetExtStr(item, bDot) if extItem.lower() == ext.lower(): listRet.append(item) return listRet # 파일 이름에서 확장자를 뽑아낸다. True : '.' 을 포함한다. def GetExtStr(file_path: str, bDot: bool = True)-> str: retStr = "" # 파일 경로에서 마지막 점을 찾아 확장자를 추출 last_dot_index = file_path.rfind('.') if last_dot_index == -1: retStr = "" # 점이 없는 경우 확장자가 없음 else: if True == bDot: retStr = file_path[last_dot_index:] else: retStr = file_path[last_dot_index+1:] return retStr # 문자열에 포함된 단어를 지운다. def RmvSubString(mainString: str, subString: str)-> str: # 문자열에서 부분 문자열의 인덱스를 찾습니다. strIdx = mainString.find(subString) if strIdx == -1: # 부분 문자열이 존재하지 않으면 그대로 반환합니다. return mainString endIdx = strIdx + len(subString) # 부분 문자열을 제거하고 새로운 문자열을 반환합니다. return mainString[:strIdx] + mainString[endIdx:] # def ExtractZIP(zip_file: str, extract_to: str): with zipfile.ZipFile(zip_file, 'r') as zf: zf.extractall(extract_to) # def CreateZIP(output_zip: str, files: list[str]) -> bool: with zipfile.ZipFile(output_zip, 'w') as zf: for file in files: pathTemp = os.path.join('root', os.path.basename(file)) zf.write(file, pathTemp) bRet = False if os.path.exists(output_zip): bRet = True return bRet # 파일 리스트에 들어있는 파일만 골라서 압축을 합니다. 상대경로를 제거하는게 기본값. def CreateZIPShell(zipName: str, files: list[str], bRmvRPath: bool = True) -> bool: command = "zip " if True == bRmvRPath: command += "-j " command += f"\"{zipName}\" " # 이중 리스트인 이유를 모르겠다. for file in files: command += f"\"{file}\" " result = subprocess.run(command, shell=True, capture_output=True, text=True) bRet = False if 0 == result.returncode: bRet = True return bRet # 특정 확장자만 쉘을 이용해서 압축한다 def CreateZIPShExt(zipName: str, TrgExt: str)-> bool: command = f"zip -j {zipName} *.{TrgExt}" result = subprocess.run(command, shell=True, capture_output=True, text=True) bRet = False if 0 == result.returncode: bRet = True return bRet # 압축 파일 내의 모든 파일 및 디렉토리 목록 가져오기 def GetZipContentList(path: str) -> list[str]: if True == IsEmptyStr(path) or not os.path.isfile(path): return [] listRet = [] with zipfile.ZipFile( path , 'r') as zip_file: listRet = zip_file.namelist() return listRet # def GetZippedFileByte(pathZip: str, FileName: str) -> bytes: retBytes:bytes = bytes() if True == os.path.isfile(pathZip): with zipfile.ZipFile( pathZip , 'r') as zip_file: # 압축 파일 내의 특정 파일을 읽기 with zip_file.open(FileName) as file: retBytes = file.read() return retBytes # JSON 을 트리 구조로 출력한다. def PrintJSONTree(data, indent: int=0 ) -> None: if isinstance(data, dict): for key, value in data.items(): print(' ' * indent + str(key)) PrintJSONTree(value, indent + 1) elif isinstance(data, list): for item in data: PrintJSONTree(item, indent) else: print(' ' * indent + str(data)) # def IsPathWithin(base_path: str, target_path: str) -> bool: base = Path(base_path).resolve() target = Path(target_path).resolve() return target.is_relative_to(base) # 랜덤 UUID 생성 def UUIDGenRandom(): random_uuid = uuid.uuid4() return random_uuid # 이름을 기반으로 UUID 생성 def UUIDGenName(SeedName:str): namespace_uuid = uuid.uuid5(uuid.NAMESPACE_DNS, SeedName) return namespace_uuid # def GetTextInBrakets(text:str)-> list[str]: return re.findall(r'\[(.*?)\]', text) #파일의 해시를 계산하는 함수. #Args: # strFilePath (str): 파일 경로 # method (str): 사용할 해시 알고리즘 ('md5', 'sha1', 'sha256' 등) #Returns: # str: 계산된 해시값 (16진수 문자열) def CalculateFileHash(strFilePath: str, method: str="sha256")-> str: funcHash = getattr(hashlib, method)() with open(strFilePath, "rb") as f: while True: chunk = f.read(4096) if not chunk: break funcHash.update(chunk) return funcHash.hexdigest() #파일의 해시를 비교하여 무결성 검증. #Args: # file_path (str): 파일 경로 # expected_hash (str): 기대하는 해시값 # method (str): 사용할 해시 알고리즘 #Returns: # bool: 파일이 정상인지 여부 def VerifyIsValidFile(strPath: str, strCompHash: str, strMethod: str="sha256")->bool: Hash_Calcd = CalculateFileHash(strPath, strMethod) return strCompHash.lower() == Hash_Calcd.lower() """ # 사용 예시 if __name__ == "__main__": file_to_check = "example.txt" known_good_hash = "5d41402abc4b2a76b9719d911017c592" # 예시 (MD5) is_valid = verify_file(file_to_check, known_good_hash, method='md5') if is_valid: print("파일이 정상입니다!") else: print("파일이 손상되었거나 다릅니다!") """