이것이 파이썬이있는 고유 한 버전의 파일 이름을 얻는 가장 좋은 방법입니까?

StackOverflow https://stackoverflow.com/questions/183480

  •  06-07-2019
  •  | 
  •  

문제

아직도 파이썬에 '다이빙'을하고, 내가 무언가를 간과하지 않기를 원합니다. 여러 ZIP 파일에서 파일을 추출하고 추출 된 파일을 하나의 디렉토리에 저장하는 스크립트를 작성했습니다. 중복 파일 이름이 잘못 작성되는 것을 방지하기 위해이 작은 기능을 썼습니다. 더 좋은 방법이 있는지 궁금합니다. 감사!

def unique_filename(file_name):
counter = 1
file_name_parts = os.path.splitext(file_name) # returns ('/path/file', '.ext')
while os.path.isfile(file_name): 
    file_name = file_name_parts[0] + '_' + str(counter) + file_name_parts[1]
    counter += 1
return file_name

나는 정말 하다 파일이 단일 디렉토리에 있어야하고 내 경우에는 번호가 매겨진 복제물이 확실히 허용되므로 더 강력한 방법을 찾고 있지는 않지만 (포인터를 환영한다고 생각합니다) 달성은 올바른 방법으로 끝나고 있습니다.

도움이 되었습니까?

해결책

한 가지 문제는 위의 코드에 인종 조건이 있다는 것입니다. 존재 테스트와 파일 생성 사이에는 차이가 있기 때문입니다. 이에 대한 보안에 영향을 줄 수 있습니다 (누군가가 교묘 한 파일에 심볼 링크를 악의적으로 삽입 할 수없는 사람에 대해 생각하지만, 더 높은 권한으로 실행할 수있는 프로그램은 OS.Tempnam과 같은 것들과 같은 공격이 발생하는 것과 같은 공격입니다. )는 더 이상 사용되지 않습니다.

이를 해결하려면 가장 좋은 방법은 실제로 실패하면 예외를 얻을 수있는 방식으로 파일을 만들고 실제로 열린 파일 개체를 반환하는 것입니다. OS.O_CREAT 및 OS.O_EXCL 플래그를 모두 전달하여 하위 레벨 OS.Open 함수로 수행 할 수 있습니다. 일단 열리면 작성한 실제 파일 (및 선택적으로 파일 이름)을 반환하십시오. 예를 들어,이 접근법을 사용하도록 수정 된 코드는 다음과 같습니다 (a (파일, 파일 이름) 튜플 리턴) :

def unique_file(file_name):
    counter = 1
    file_name_parts = os.path.splitext(file_name) # returns ('/path/file', '.ext')
    while 1:
        try:
            fd = os.open(file_name, os.O_CREAT | os.O_EXCL | os.O_RDRW)
            return os.fdopen(fd), file_name
        except OSError:
            pass
        file_name = file_name_parts[0] + '_' + str(counter) + file_name_parts[1]
        counter += 1

편집하다 실제로 위의 문제를 처리 할 수있는 더 나은 방법은 아마도 템 파일 모듈을 사용하는 것입니다. 다음은 사용의 예입니다 (유사한 인터페이스 유지) :

def unique_file(file_name):
    dirname, filename = os.path.split(file_name)
    prefix, suffix = os.path.splitext(filename)

    fd, filename = tempfile.mkstemp(suffix, prefix+"_", dirname)
    return os.fdopen(fd), filename

>>> f, filename=unique_file('/home/some_dir/foo.txt')
>>> print filename
/home/some_dir/foo_z8f_2Z.txt

이 접근법의 유일한 단점은 수정되지 않은 파일 (/home/some_dir/foo.txt)을 먼저 만들려고 시도하지 않기 때문에 항상 임의의 문자가있는 파일 이름을 얻을 수 있다는 것입니다. 또한 Tempfile.temporaryFile 및 명명 된 temporaryFile을보고 싶을 수도 있습니다. 위의 작업을 수행하고 닫을 때 디스크에서 자동으로 삭제됩니다.

다른 팁

예, 이것은 읽기 쉽지만 독특한 파일 이름을위한 좋은 전략입니다.

하나의 중요한 변화: 교체해야합니다 os.path.isfile ~와 함께 os.path.lexists! 지금 작성된대로 /foo/bar.baz라는 디렉토리가 있으면 프로그램이 새 파일 (작동하지 않음)으로이를 덮어 쓰려고합니다. isfile 디렉토리가 아닌 파일 만 확인합니다. lexists 디렉토리, Symlinks 등에 대한 확인 ... 기본적으로 파일 이름을 만들 수없는 이유가있는 경우.

편집 : @Brian은 더 나은 답변을 제공했으며, 이는 인종 조건 측면에서 더 안전하고 강력합니다.

두 가지 작은 변화 ...

base_name, ext = os.path.splitext(file_name) 

당신은 뚜렷한 의미로 두 가지 결과를 얻습니다.

file_name = "%s_%d%s" % (base_name, str(counter), ext)

더 빠르거나 상당히 짧지 않습니다. 그러나 파일 이름 패턴을 변경하려면 패턴이 한 곳에 있으며 작업하기가 약간 쉽습니다.

읽을 수있는 이름을 원한다면 이것은 좋은 솔루션처럼 보입니다.
예를 들어 고유 한 파일 이름을 반환하는 루틴이 있습니다. 온도 파일이지만 긴 임의의 이름을 생성합니다.

가독성에 관심이 없다면 uuid.uuid4 ()가 친구입니다.

import uuid

def unique_filename(prefix=None, suffix=None):
    fn = []
    if prefix: fn.extend([prefix, '-'])
    fn.append(str(uuid.uuid4()))
    if suffix: fn.extend(['.', suffix.lstrip('.')])
    return ''.join(fn)

어때

def ensure_unique_filename(orig_file_path):    
    from time import time
    import os

    if os.path.lexists(orig_file_path):
        name, ext = os.path.splitext(orig_file_path)
        orig_file_path = name + str(time()).replace('.', '') + ext

    return orig_file_path

시간 ()은 현재 시간을 밀리 초로 반환합니다. 원래 파일 이름과 결합하여 복잡한 멀티 스레드 케이스에서도 상당히 독특합니다.

라이센스 : CC-BY-SA ~와 함께 속성
제휴하지 않습니다 StackOverflow
scroll top