Trasformare una stringa in un nome file valido?


298

Ho una stringa che voglio usare come nome file, quindi voglio rimuovere tutti i caratteri che non sarebbero ammessi nei nomi dei file, usando Python.

Preferirei essere severo che altrimenti, quindi diciamo che voglio conservare solo lettere, cifre e un piccolo insieme di altri personaggi come "_-.() ". Qual è la soluzione più elegante?

Il nome file deve essere valido su più sistemi operativi (Windows, Linux e Mac OS): è un file MP3 nella mia libreria con il titolo del brano come nome file ed è condiviso e sottoposto a backup tra 3 macchine.


17
Non dovrebbe essere integrato nel modulo os.path?
endolito il

2
Forse, sebbene il suo caso d'uso richiederebbe un singolo percorso sicuro su tutte le piattaforme, non solo quello attuale, che è qualcosa che os.path non è progettato per gestire.
javawizard,

2
Per espandere il commento sopra: l'attuale progetto di os.pathcarica effettivamente una libreria diversa a seconda del sistema operativo (vedere la seconda nota nella documentazione ). Quindi, se una funzione di quotatura è stata implementata os.path, potrebbe citare solo la stringa per la sicurezza POSIX quando si esegue su un sistema POSIX o per la sicurezza di Windows quando si esegue su Windows. Il nome file risultante non sarebbe necessariamente valido su entrambe le finestre e POSIX, che è ciò che la domanda richiede.
pastore

Risposte:


164

Puoi guardare il framework Django per come creano una "lumaca" da un testo arbitrario. Una lumaca è compatibile con URL e nome file.

I programmi di utilità Django definiscono una funzione, slugify()che è probabilmente il gold standard per questo tipo di cose. In sostanza, il loro codice è il seguente.

def slugify(value):
    """
    Normalizes string, converts to lowercase, removes non-alpha characters,
    and converts spaces to hyphens.
    """
    import unicodedata
    value = unicodedata.normalize('NFKD', value).encode('ascii', 'ignore')
    value = unicode(re.sub('[^\w\s-]', '', value).strip().lower())
    value = unicode(re.sub('[-\s]+', '-', value))
    # ...
    return value

C'è di più, ma l'ho lasciato fuori, dal momento che non si rivolge alla lumaca, ma alla fuga.


11
L'ultima riga dovrebbe essere: valore = unicode (re.sub ('[- \ s] +', '-', valore))
Joseph Turian

1
Grazie - Potrei mancare qualcosa, ma sto ottenendo: "L'argomento 2 normalize () deve essere unicode, non str"
Alex Cook,

"normalize () argomento 2". Significa il value. Se il valore deve essere Unicode, devi assicurarti che sia effettivamente Unicode. O. Potresti voler tralasciare la normalizzazione unicode se il tuo valore reale è in realtà una stringa ASCII.
S. Lott,

8
Nel caso in cui qualcuno non abbia notato il lato positivo di questo approccio è che non solo rimuove i caratteri non alfa, ma tenta prima di trovare buoni sostituti (tramite la normalizzazione NFKD), quindi é diventa e, un apice 1 diventa un normale 1, ecc. Grazie
Michael Scott Cuthbert l'

48
La slugifyfunzione è stata spostata in django / utils / text.py e quel file contiene anche una get_valid_filenamefunzione.
Denilson Sá Maia,

104

Questo approccio alla whitelist (ovvero, che consente solo i caratteri presenti in valid_chars) funzionerà se non ci sono limiti alla formattazione dei file o alla combinazione di caratteri validi che sono illegali (come ".."), ad esempio, ciò che dici consentirebbe un nome file ". txt" che penso non sia valido su Windows. Poiché questo è l'approccio più semplice, proverei a rimuovere gli spazi bianchi da valid_chars e anteporre una stringa valida nota in caso di errore, qualsiasi altro approccio dovrà sapere cosa è permesso dove far fronte alle limitazioni di denominazione dei file di Windows e quindi essere molto più complesso.

>>> import string
>>> valid_chars = "-_.() %s%s" % (string.ascii_letters, string.digits)
>>> valid_chars
'-_.() abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789'
>>> filename = "This Is a (valid) - filename%$&$ .txt"
>>> ''.join(c for c in filename if c in valid_chars)
'This Is a (valid) - filename .txt'

7
valid_chars = frozenset(valid_chars)non farebbe male. È 1,5 volte più veloce se applicato a allchars.
jfs,

2
Avviso: questa mappa associa due stringhe diverse alla stessa stringa >>> stringa di importazione >>> valid_chars = "- . ()% S% s"% (string.ascii_letters, string.digits) >>> valid_chars '- . () abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789 '>>> nome file = "a.com/hello/world" >>>' '.join (c per c nel nome file se c in valid_chars)' a.comhelloworld '>>> nomefile = "ahellow ">>> '' .join (c per c nel nome file se c in valid_chars) 'a.comhelloworld' >>>
robert king

3
Per non parlare del fatto che nominare un file "CON"su Windows ti metterà nei guai ...
Nathan Osman,

2
Una leggera riorganizzazione rende semplice specificare un carattere sostitutivo. Innanzitutto la funzionalità originale: '' .join (c se c in valid_chars else '' per c nel nome file) o con un carattere o una stringa sostituiti per ogni carattere non valido: '' .join (c se c in valid_chars else '.' Per c nel nome file)
PeterVermont,

101

È possibile utilizzare la comprensione dell'elenco insieme ai metodi stringa.

>>> s
'foo-bar#baz?qux@127/\\9]'
>>> "".join(x for x in s if x.isalnum())
'foobarbazqux1279'

3
Nota che puoi omettere le parentesi quadre. In questo caso , un'espressione del generatore viene passata al join, il che salva il passaggio della creazione di un elenco altrimenti inutilizzato.
Oben Sonne,

31
+1 Mi è piaciuto molto. Leggera modifica che ho fatto: "" .join ([x if x.isalnum () else "_" per x in s]) - produrrebbe un risultato in cui gli elementi non validi sono _, come se fossero oscurati. Forse aiuta qualcuno.
Eddie Parker,

12
Questa soluzione è fantastica! Ho apportato una leggera modifica però:filename = "".join(i for i in s if i not in "\/:*?<>|")
Alex Krycek il

1
Purtroppo non consente nemmeno spazi e punti, ma mi piace l'idea.
tiktak,

9
@tiktak: per (anche) consentire spazi, punti e caratteri di sottolineatura che puoi scegliere"".join( x for x in s if (x.isalnum() or x in "._- "))
liscio

95

Qual è la ragione per usare le stringhe come nomi di file? Se la leggibilità umana non è un fattore, andrei con il modulo base64 che può produrre stringhe sicure di file system. Non sarà leggibile ma non dovrai affrontare le collisioni ed è reversibile.

import base64
file_name_string = base64.urlsafe_b64encode(your_string)

Aggiornamento : modificato in base al commento di Matthew.


1
Ovviamente questa è la risposta migliore in tal caso.
user32141

60
Avvertimento! La codifica base64 per impostazione predefinita include il carattere "/" come output valido che non è valido nei nomi di file su molti sistemi. Utilizza invece base64.urlsafe_b64encode (your_string)
Matthew,

15
La leggibilità in realtà umana è quasi sempre un fattore, anche se solo per scopi di debug.
static_rtti,

5
In Python 3 your_stringdeve essere un array di byte o il risultato di encode('ascii')questo per funzionare.
Noumenon,

4
def url2filename(url): url = url.encode('UTF-8') return base64.urlsafe_b64encode(url).decode('UTF-8') def filename2url(f): return base64.urlsafe_b64decode(f).decode('UTF-8')
JeffProd,

40

Solo per complicare ulteriormente le cose, non è garantito ottenere un nome file valido semplicemente rimuovendo i caratteri non validi. Poiché i caratteri consentiti differiscono in base a nomi di file diversi, un approccio conservativo potrebbe finire per trasformare un nome valido in uno non valido. Potresti voler aggiungere una gestione speciale per i casi in cui:

  • La stringa è composta da tutti i caratteri non validi (lasciandoti con una stringa vuota)

  • Si finisce con una stringa con un significato speciale, ad esempio "." o ".."

  • Su Windows, alcuni nomi di dispositivi sono riservati. Ad esempio, non è possibile creare un file denominato "nul", "nul.txt" (o nul.tutto in realtà) I nomi riservati sono:

    CON, PRN, AUX, NUL, COM1, COM2, COM3, COM4, ​​COM5, COM6, COM7, COM8, COM9, LPT1, LPT2, LPT3, LPT4, LPT5, LPT6, LPT7, LPT8 e LPT9

Probabilmente puoi aggirare questi problemi anteponendo una stringa ai nomi dei file che non possono mai risultare in uno di questi casi e rimuovendo i caratteri non validi.


25

C'è un bel progetto su Github chiamato python-slugify :

Installare:

pip install python-slugify

Quindi utilizzare:

>>> from slugify import slugify
>>> txt = "This\ is/ a%#$ test ---"
>>> slugify(txt)
'this-is-a-test'

2
Mi piace questa libreria ma non è così buona come pensavo. Test iniziale ok ma converte anche punti. Quindi test.txtottiene test-txtche è troppo.
therealmarv,

23

Proprio come ha risposto S.Lott , puoi guardare il Django Framework per come convertono una stringa in un nome file valido.

La versione più recente e aggiornata si trova in utils / text.py e definisce "get_valid_filename", che è il seguente:

def get_valid_filename(s):
    s = str(s).strip().replace(' ', '_')
    return re.sub(r'(?u)[^-\w.]', '', s)

(Vedi https://github.com/django/django/blob/master/django/utils/text.py )


4
per i più pigri già in django:django.utils.text import get_valid_filename
theannouncer

2
Nel caso in cui non si abbia familiarità con regex, re.sub(r'(?u)[^-\w.]', '', s)rimuove tutti i caratteri che non sono lettere, non numeri (0-9), non il trattino basso ('_'), non il trattino ('-') e non il punto ('.' ). "Lettere" qui include tutte le lettere unicode, come 漢語.
cowlinator,

3
Puoi anche verificare la lunghezza: i nomi dei file sono limitati a 255 caratteri (o, sai, 32; a seconda dell'FS)
Matthias Winkelmann

19

Questa è la soluzione che alla fine ho usato:

import unicodedata

validFilenameChars = "-_.() %s%s" % (string.ascii_letters, string.digits)

def removeDisallowedFilenameChars(filename):
    cleanedFilename = unicodedata.normalize('NFKD', filename).encode('ASCII', 'ignore')
    return ''.join(c for c in cleanedFilename if c in validFilenameChars)

La chiamata unicodedata.normalize sostituisce i caratteri accentati con l'equivalente non accentato, che è meglio che semplicemente eliminarli. Successivamente vengono rimossi tutti i caratteri non consentiti.

La mia soluzione non antepone una stringa nota per evitare possibili nomi di file non consentiti, perché so che non possono verificarsi a causa del mio particolare formato di nome file. Una soluzione più generale dovrebbe farlo.


dovresti essere in grado di usare uuid.uuid4 () per il tuo prefisso univoco
slf

6
caso di cammello .. ahh
riccio demenziale

Questo potrebbe essere modificato / aggiornato per funzionare con Python 3.6?
Wavesailor

13

Tieni presente che in realtà non ci sono restrizioni sui nomi dei file sui sistemi Unix diversi da

  • Potrebbe non contenere \ 0
  • Potrebbe non contenere /

Tutto il resto è un gioco equo.

$ touch "
> anche multilinea
> haha
> ^ [[31m rosso ^ [[0m
> male "
$ ls -la 
-rw-r - r-- 0 17 nov 23:39? anche multilinea? haha ​​?? [31m rosso? [0m? male
$ ls -lab
-rw-r - r-- 0 nov 17 23:39 \ neven \ multiline \ nhaha \ n \ 033 [31m \ red \ \ 033 [0m \ nevil
$ perl -e 'per il mio $ i (glob (q {./* pari *})) {print $ i; } "
./
anche multilinea
haha
 rosso 
il male

Sì, ho appena archiviato i codici colore ANSI in un nome file e li ho resi effettivi.

Per l'intrattenimento, inserisci un carattere BEL in un nome di directory e guarda il divertimento che ne consegue quando si inserisce il CD;)


L'OP afferma che "Il nome file deve essere valido su più sistemi operativi"
cowlinator

1
@cowlinator che il chiarimento è stato aggiunto 10 ore dopo che la mia risposta è stata pubblicata :) Controllare il registro di modifica dell'OP.
Kent Fredric,

12

In una riga:

valid_file_name = re.sub('[^\w_.)( -]', '', any_string)

puoi anche inserire il carattere '_' per renderlo più leggibile (ad esempio in caso di sostituzione di barre)


7

È possibile utilizzare il metodo re.sub () per sostituire qualsiasi cosa non "filelike". In effetti, ogni personaggio potrebbe essere valido; quindi non ci sono funzioni predefinite (credo), per farlo.

import re

str = "File!name?.txt"
f = open(os.path.join("/tmp", re.sub('[^-a-zA-Z0-9_.() ]+', '', str))

Si tradurrebbe in un filehandle in /tmp/filename.txt.


5
È necessario il trattino per andare per primo nel matcher del gruppo in modo che non appaia come un intervallo. re.sub ('[^ - a-zA-Z0-9 _. ()] +', '', str)
phord

7
>>> import string
>>> safechars = bytearray(('_-.()' + string.digits + string.ascii_letters).encode())
>>> allchars = bytearray(range(0x100))
>>> deletechars = bytearray(set(allchars) - set(safechars))
>>> filename = u'#ab\xa0c.$%.txt'
>>> safe_filename = filename.encode('ascii', 'ignore').translate(None, deletechars).decode()
>>> safe_filename
'abc..txt'

Non gestisce stringhe vuote, nomi di file speciali ('nul', 'con', ecc.).


+1 per le tabelle di traduzione, è di gran lunga il metodo più efficiente. Per i nomi di file speciali / vuoti, sarà sufficiente un semplice controllo delle condizioni preliminari e per periodi estranei anche questa è una semplice correzione.
Christian Witts,

1
Mentre tradurre è leggermente più efficiente di una regexp, molto probabilmente il tempo sarà ridotto se si tenta effettivamente di aprire il file, il che senza dubbio si intende fare. Quindi preferisco più una soluzione regexp più leggibile rispetto al pasticcio sopra
nosatalian

Sono anche preoccupato per la lista nera. Certo, è una lista nera basata su una whitelist, ma comunque. Sembra meno ... sicuro. Come fai a sapere che "allchars" è effettivamente completo?
isaaclw,

@isaaclw: '.translate ()' accetta una stringa da 256 caratteri come tabella di traduzione (traduzione da byte a byte). '.maketrans ()' crea tale stringa. Tutti i valori sono coperti; è un puro approccio whitelist
jfs

Che dire del nome file "." (un singolo punto). Ciò non funzionerebbe su Unix poiché l'attuale directory sta usando quel nome.
Finn Årup Nielsen,

6

Anche se devi stare attento. Non è chiaramente detto nella tua introduzione, se stai guardando solo la lingua latina. Alcune parole possono diventare insignificanti o un altro significato se le si disinfetta solo con caratteri ASCII.

immagina di avere "forêt poésie" (poesia forestale), la tua sanificazione potrebbe dare "fort-posie" (forte + qualcosa di insignificante)

Peggio ancora se hai a che fare con personaggi cinesi.

"下 北 沢" il tuo sistema potrebbe finire per fare "---" che è destinato a fallire dopo un po 'e non molto utile. Quindi, se hai a che fare solo con i file, ti incoraggio a chiamarli una catena generica che controlli o a mantenere i personaggi così come sono. Per gli URI, più o meno lo stesso.


6

Perché non semplicemente avvolgere "osopen" con un tentativo / salvo e lasciare che il sistema operativo sottostante risolva se il file è valido?

Sembra molto meno lavoro ed è valido indipendentemente dal sistema operativo in uso.


5
Valida il nome però? Voglio dire, se il sistema operativo non è felice, allora devi ancora fare qualcosa, giusto?
Jeromej,

1
In alcuni casi, il sistema operativo / la lingua potrebbero silenziosamente spostare il tuo nome file in una forma alternativa, ma quando fai un elenco di directory, otterrai un nome diverso. E questo può portare a un problema "quando scrivo il file è lì, ma quando cerco il file si chiama qualcos'altro". (Sto parlando di comportamenti di cui ho sentito parlare su VAX ...)
Kent Fredric,

Inoltre, "Il nome file deve essere valido su più sistemi operativi", che non è possibile rilevare con l' osopenesecuzione su una macchina.
LarsH

5

Un altro problema che gli altri commenti non hanno ancora affrontato è la stringa vuota, che ovviamente non è un nome file valido. Puoi anche finire con una stringa vuota togliendo troppi caratteri.

Cosa succede con i nomi di file riservati di Windows e i problemi con i punti, la risposta più sicura alla domanda "come posso normalizzare un nome file valido dall'input dell'utente arbitrario?" è "non preoccuparti nemmeno di provare": se riesci a trovare un altro modo per evitarlo (ad es. usando chiavi primarie intere da un database come nomi di file), fallo.

Se è necessario, e hai davvero bisogno di consentire spazi e '.' per le estensioni di file come parte del nome, prova qualcosa del tipo:

import re
badchars= re.compile(r'[^A-Za-z0-9_. ]+|^\.|\.$|^ | $|^$')
badnames= re.compile(r'(aux|com[1-9]|con|lpt[1-9]|prn)(\.|$)')

def makeName(s):
    name= badchars.sub('_', s)
    if badnames.match(name):
        name= '_'+name
    return name

Anche questo non può essere garantito proprio su sistemi operativi inaspettati, ad esempio il sistema operativo RISC odia gli spazi e usa "." come separatore di directory.


4

Mi è piaciuto l'approccio python-slugify qui ma era anche eliminare i punti che non era desiderato. Quindi l'ho ottimizzato per il caricamento di un nome file pulito su s3 in questo modo:

pip install python-slugify

Codice di esempio:

s = 'Very / Unsafe / file\nname hähä \n\r .txt'
clean_basename = slugify(os.path.splitext(s)[0])
clean_extension = slugify(os.path.splitext(s)[1][1:])
if clean_extension:
    clean_filename = '{}.{}'.format(clean_basename, clean_extension)
elif clean_basename:
    clean_filename = clean_basename
else:
    clean_filename = 'none' # only unclean characters

Produzione:

>>> clean_filename
'very-unsafe-file-name-haha.txt'

Questo è così sicuro, funziona con nomi di file senza estensione e funziona anche solo con nomi di file di caratteri non sicuri (il risultato è nonequi).


1
Mi piace questo, non reinventare la ruota, non importare l'intero framework Django se non ne hai bisogno, non incollare direttamente il codice se non lo manterrai in futuro, e le stringhe generate provano per abbinare lettere simili a lettere sicure, quindi la nuova stringa è più facile da leggere.
Vicenteherrera,

1
Per usare la sottolineatura invece del trattino: name = slugify (s, separator = '_')
vicenteherrera

3

Risposta modificata per Python 3.6

import string
import unicodedata

validFilenameChars = "-_.() %s%s" % (string.ascii_letters, string.digits)
def removeDisallowedFilenameChars(filename):
    cleanedFilename = unicodedata.normalize('NFKD', filename).encode('ASCII', 'ignore')
    return ''.join(chr(c) for c in cleanedFilename if chr(c) in validFilenameChars)

Potresti spiegare la tua risposta in dettaglio?
Serenità,

È la stessa risposta accettata da Sophie Gage. Ma è stato modificato per funzionare su Python 3.6
Jean-Robin Tremblay il

2

Mi rendo conto che ci sono molte risposte, ma si basano principalmente su espressioni regolari o moduli esterni, quindi vorrei inserire la mia risposta. Una pura funzione Python, nessun modulo esterno necessario, nessuna espressione regolare utilizzata. Il mio approccio non è quello di pulire i caratteri non validi, ma di consentire solo quelli validi.

def normalizefilename(fn):
    validchars = "-_.() "
    out = ""
    for c in fn:
      if str.isalpha(c) or str.isdigit(c) or (c in validchars):
        out += c
      else:
        out += "_"
    return out    

se lo desideri, puoi aggiungere i tuoi caratteri validi alla validcharsvariabile all'inizio, come le lettere nazionali che non esistono in alfabeto inglese. Questo è qualcosa che potresti o non vuoi: alcuni file system che non funzionano su UTF-8 potrebbero avere ancora problemi con caratteri non ASCII.

Questa funzione serve per verificare la validità di un singolo nome file, quindi sostituirà i separatori di percorso con _ considerandoli caratteri non validi. Se si desidera aggiungere ciò, è banale modificare il ifseparatore di percorso del sistema operativo da includere.


1

Molte di queste soluzioni non funzionano.

'/ hello / world' -> 'helloworld'

'/ helloworld' / -> 'helloworld'

Questo non è ciò che desideri in generale, supponiamo che stai salvando l'html per ogni link, sovrascriverai l'html per una diversa pagina web.

Pickle un dict come:

{'helloworld': 
    (
    {'/hello/world': 'helloworld', '/helloworld/': 'helloworld1'},
    2)
    }

2 rappresenta il numero che deve essere aggiunto al nome file successivo.

Cerco il nome del file ogni volta dal dict. Se non è presente, ne creo uno nuovo, aggiungendo il numero massimo, se necessario.


nota, se usi helloworld1, devi anche controllare che helloworld1 non sia in uso e così via ..
robert king

1

Non è esattamente ciò che OP stava chiedendo, ma questo è quello che uso perché ho bisogno di conversioni uniche e reversibili:

# p3 code
def safePath (url):
    return ''.join(map(lambda ch: chr(ch) if ch in safePath.chars else '%%%02x' % ch, url.encode('utf-8')))
safePath.chars = set(map(lambda x: ord(x), '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz+-_ .'))

Il risultato è "in qualche modo" leggibile, almeno dal punto di vista di un amministratore di sistema.


Un wrapper per questo senza spazi nei nomi dei file:def safe_filename(filename): return safePath(filename.strip().replace(' ','_'))
SpeedCoder5

1

Se non ti dispiace installare un pacchetto, questo dovrebbe essere utile: https://pypi.org/project/pathvalidate/

Da https://pypi.org/project/pathvalidate/#sanitize-a-filename :

from pathvalidate import sanitize_filename

fname = "fi:l*e/p\"a?t>h|.t<xt"
print(f"{fname} -> {sanitize_filename(fname)}\n")
fname = "\0_a*b:c<d>e%f/(g)h+i_0.txt"
print(f"{fname} -> {sanitize_filename(fname)}\n")

Produzione

fi:l*e/p"a?t>h|.t<xt -> filepath.txt
_a*b:c<d>e%f/(g)h+i_0.txt -> _abcde%f(g)h+i_0.txt

0

Sono sicuro che questa non è una grande risposta, poiché modifica la stringa su cui si sta ripetendo, ma sembra funzionare bene:

import string
for chr in your_string:
 if chr == ' ':
   your_string = your_string.replace(' ', '_')
 elif chr not in string.ascii_letters or chr not in string.digits:
    your_string = your_string.replace(chr, '')

Ho trovato questo "".join( x for x in s if (x.isalnum() or x in "._- "))su questo post commenti
SergioAraujo,

0

AGGIORNARE

Tutti i collegamenti interrotti irreparabilmente in questa risposta di 6 anni.

Inoltre, non lo farei più in questo modo, basta base64codificare o eliminare caratteri non sicuri. Esempio di Python 3:

import re
t = re.compile("[a-zA-Z0-9.,_-]")
unsafe = "abc∂éåß®∆˚˙©¬ñ√ƒµ©∆∫ø"
safe = [ch for ch in unsafe if t.match(ch)]
# => 'abc'

Con base64te puoi codificare e decodificare, in modo da poter recuperare di nuovo il nome file originale.

Ma a seconda del caso d'uso potrebbe essere meglio generare un nome file casuale e archiviare i metadati in file o DB separati.

from random import choice
from string import ascii_lowercase, ascii_uppercase, digits
allowed_chr = ascii_lowercase + ascii_uppercase + digits

safe = ''.join([choice(allowed_chr) for _ in range(16)])
# => 'CYQ4JDKE9JfcRzAZ'

RISPOSTA ORIGINALE LINKROTTEN :

Il bobcatprogetto contiene un modulo Python che fa proprio questo.

Non è completamente affidabile, vedi questo post e questa risposta .

Quindi, come notato: la base64codifica è probabilmente un'idea migliore se la leggibilità non ha importanza.


Tutti i collegamenti sono morti. Amico, fai qualcosa.
The Peaceful Coder,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.