2012-12-13 19 views
6

Python ha qualche funzionalità integrata per questo? La mia idea è che funzionerebbe come certi sistemi operativi funzionano se un file viene emesso in una directory in cui esiste già un file con quel nome. I.e: se esiste "file.pdf" verrà creato "file2.pdf" e la prossima volta "file3.pdf".python: Crea file ma se il nome esiste aggiungi numero

+0

Questo sarebbe in generale la funzione dell'applicazione o del programma di creare i file in modo no, non v'è alcuna funzionalità native come questo . Data la directory e il nome del file puoi creare qualcosa da te. – timc

+1

controlla questo http://code.activestate.com/recipes/578116-move-files-with-rename-if-required/ – avasal

+0

controlla ['nomefile_fix_esistenza (nomefile)'] (https://github.com/steveeJ /python-wget/blob/master/wget.py#L72) –

risposta

7

In un certo senso, Python ha questa funzionalità integrata nel modulo tempfile. Sfortunatamente, devi inserire una variabile globale privata, tempfile._name_sequence. Ciò significa che ufficialmente, tempfile non garantisce che nelle versioni future _name_sequence esista anche - è un dettaglio di implementazione. Ma se si sta bene con l'utilizzo di esso in ogni caso, questo mostra come è possibile creare file denominati in modo univoco della forma file#.pdf in una directory specificata come /tmp:

import tempfile 
import itertools as IT 
import os 

def uniquify(path, sep = ''): 
    def name_sequence(): 
     count = IT.count() 
     yield '' 
     while True: 
      yield '{s}{n:d}'.format(s = sep, n = next(count)) 
    orig = tempfile._name_sequence 
    with tempfile._once_lock: 
     tempfile._name_sequence = name_sequence() 
     path = os.path.normpath(path) 
     dirname, basename = os.path.split(path) 
     filename, ext = os.path.splitext(basename) 
     fd, filename = tempfile.mkstemp(dir = dirname, prefix = filename, suffix = ext) 
     tempfile._name_sequence = orig 
    return filename 

print(uniquify('/tmp/file.pdf')) 
+0

Grazie per la risposta! Tricky roba da capire dai documenti;) Sto optando per il mio approccio, più semplice, ma questa risposta risponde chiaramente a quello che stavo chiedendo – Parham

+0

Sì, questa è probabilmente una scelta saggia se non hai bisogno delle capacità speciali di 'tempfile '. Il modulo 'tempfile' si prende cura di evitare certe condizioni di gara, sicurezza e attacchi denial of service. L'uso della numerazione sequenziale rende il codice sopra vulnerabile a un attacco Denial of Service.E non sono del tutto sicuro che quanto sopra sia al sicuro dalle condizioni di gara o da altri rischi per la sicurezza. – unutbu

1

stavo cercando di implementare la stessa cosa nel mio progetto ma la risposta di @ unutbu sembrava troppo 'pesante' per le mie esigenze così mi si avvicinò con seguente codice infine:

import os 
index = '' 
while True: 
    try: 
     os.makedirs('../hi'+index) 
     break 
    except WindowsError: 
     if index: 
      index = '('+str(int(index[1:-1])+1)+')' # Append 1 to number in brackets 
     else: 
      index = '(1)' 
     pass # Go and try create file again 

Solo nel caso qualcuno imbattuto in questo e richiede qualcosa di più semplice.

0

Poiché il tempfile hack A) è un hack e B) richiede comunque una discreta quantità di codice, sono andato con un'implementazione manuale. È fondamentalmente bisogno:

  1. Un modo per Safely create a file if and only if it does not exist (questo è ciò che l'hack tempfile ci offre).
  2. Un generatore di nomi di file.
  3. Una funzione di avvolgimento per nascondere il pasticcio.

ho definito un safe_open che può essere utilizzato proprio come aperta:

def iter_incrementing_file_names(path): 
    """ 
    Iterate incrementing file names. Start with path and add " (n)" before the 
    extension, where n starts at 1 and increases. 

    :param path: Some path 
    :return: An iterator. 
    """ 
    yield path 
    prefix, ext = os.path.splitext(path) 
    for i in itertools.count(start=1, step=1): 
     yield prefix + ' ({0})'.format(i) + ext 


def safe_open(path, mode): 
    """ 
    Open path, but if it already exists, add " (n)" before the extension, 
    where n is the first number found such that the file does not already 
    exist. 

    Returns an open file handle. Make sure to close! 

    :param path: Some file name. 

    :return: Open file handle... be sure to close! 
    """ 
    flags = os.O_CREAT | os.O_EXCL | os.O_WRONLY 

    if 'b' in mode and platform.system() == 'Windows': 
     flags |= os.O_BINARY 

    for filename in iter_incrementing_file_names(path): 
     try: 
      file_handle = os.open(filename, flags) 
     except OSError as e: 
      if e.errno == errno.EEXIST: 
       pass 
      else: 
       raise 
     else: 
      return os.fdopen(file_handle, mode) 

# Example 
with safe_open("some_file.txt", "w") as fh: 
    print("Hello", file=fh) 
0

non ho ancora testato questo ma dovrebbe funzionare, l'iterazione di possibili nomi di file fino a quando il file in questione non esiste in quale punto si rompe.

def increment_filename(fn): 
    fn, extension = os.path.splitext(path) 

    n = 1 
    yield fn + extension 
    for n in itertools.count(start=1, step=1) 
     yield '%s%d.%s' % (fn, n, extension) 

for filename in increment_filename(original_filename): 
    if not os.isfile(filename): 
     break 
0

Recentemente ho incontrato la stessa cosa e qui è il mio approccio:

import os 

file_name = "file_name.txt" 
if os.path.isfile(file_name): 
    expand = 1 
    while True: 
     expand += 1 
     new_file_name = file_name.split(".txt")[0] + str(expand) + ".txt" 
     if os.path.isfile(new_file_name): 
      continue 
     else: 
      file_name = new_file_name 
      break 
0

Questo funziona per me. Il nome del file iniziale è 0.yml, se esiste, si aggiungerà uno fino a soddisfare il requisito

import os 
import itertools 

def increment_filename(file_name): 
    fid, extension = os.path.splitext(file_name) 

    yield fid + extension 
    for n in itertools.count(start=1, step=1): 
     new_id = int(fid) + n 
     yield "%s%s" % (new_id, extension) 


def get_file_path(): 
    target_file_path = None 
    for file_name in increment_filename("0.yml"): 
     file_path = os.path.join('/tmp', file_name) 
     if not os.path.isfile(file_path): 
      target_file_path = file_path 
      break 
    return target_file_path