IT

문자열을 유효한 파일 이름으로 바꾸시겠습니까?

lottoking 2020. 3. 28. 10:45
반응형

문자열을 유효한 파일 이름으로 바꾸시겠습니까?


파일 이름으로 사용하려는 문자열이 있으므로 Python을 사용하여 파일 이름에 허용되지 않는 모든 문자를 제거하고 싶습니다.

나는 다른 것보다 엄격하기 때문에 문자, 숫자 및와 같은 작은 다른 문자 세트 만 유지하고 싶다고 가정 해 봅시다 "_-.() ". 가장 우아한 솔루션은 무엇입니까?

파일 이름은 여러 운영 체제 (Windows, Linux 및 Mac OS)에서 유효해야합니다. 파일 이름이 노래 제목 인 내 라이브러리의 MP3 파일이며 3 대의 컴퓨터간에 공유 및 백업됩니다.


Django 프레임 워크 에서 임의의 텍스트로 "슬러그"를 만드는 방법을 살펴볼 수 있습니다 . 슬러그는 URL 및 파일 이름 친화적입니다.

Django 텍스트 유틸리티는 함수를 정의합니다. slugify()아마도 이런 종류의 표준 일 것입니다. 기본적으로 코드는 다음과 같습니다.

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))

더 많은 것이 있지만, 제련을 다루지 않고 탈출하기 때문에 제외했습니다.


이 화이트리스트 방식 (즉, valid_chars에있는 문자 만 허용)은 파일 형식이나 ".."와 같이 잘못된 유효한 문자 조합 (예 : "..")에 제한이없는 경우 작동합니다. Windows에서 유효하지 않다고 생각되는 ".txt"라는 파일 이름을 허용합니다. 이것이 가장 간단한 접근법이므로 valid_chars에서 공백을 제거하고 오류가 발생하는 경우 알려진 유효한 문자열을 추가하려고 시도하므로 다른 접근법은 Windows 파일 이름 지정 제한 에 대처할 수있는 위치에 대해 알아야 하므로 훨씬 더 복잡합니다.

>>> 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'

문자열 메서드와 함께 목록 이해를 사용할 수 있습니다.

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

문자열을 파일 이름으로 사용하는 이유는 무엇입니까? 사람의 가독성이 중요하지 않은 경우 파일 시스템 안전 문자열을 생성 할 수있는 base64 모듈을 사용합니다. 읽을 수는 없지만 충돌을 처리 할 필요가 없으며 가역적입니다.

import base64
file_name_string = base64.urlsafe_b64encode(your_string)

업데이트 : Matthew의 의견에 따라 변경되었습니다.


더 복잡하게 만들기 위해 잘못된 문자를 제거하여 유효한 파일 이름을 얻을 수는 없습니다. 허용되는 문자는 파일 이름이 다르기 때문에 보수적 인 접근 방식으로 인해 유효한 이름을 잘못된 이름으로 바꿀 수 있습니다. 다음과 같은 경우에 특수 처리를 추가 할 수 있습니다.

  • 문자열은 모두 유효하지 않은 문자입니다 (빈 문자열로 남겨 두십시오)

  • "."와 같은 특별한 의미의 문자열로 끝납니다. 또는 ".."

  • Windows에서는 특정 장치 이름 이 예약되어 있습니다. 예를 들어 "nul", "nul.txt"(또는 실제로 nul.anything)라는 파일을 만들 수 없습니다. 예약 된 이름은 다음과 같습니다.

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

파일 이름 앞에 문자열을 추가하여 이러한 경우 중 하나를 초래할 수 없으며 유효하지 않은 문자를 제거하여 이러한 문제를 해결할 수 있습니다.


Github에는 python-slugify 라는 멋진 프로젝트가 있습니다 .

설치:

pip install python-slugify

그런 다음 사용하십시오.

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

이것이 내가 궁극적으로 사용한 솔루션입니다.

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)

unicodedata.normalize 호출은 악센트 부호가있는 문자를 액센트가없는 문자로 대체하므로 단순히 문자를 제거하는 것보다 낫습니다. 그런 다음 허용되지 않는 모든 문자가 제거됩니다.

내 솔루션은 허용되지 않는 파일 이름을 피하기 위해 알려진 문자열을 앞에 두지 않습니다. 왜냐하면 특정 파일 이름 형식으로 인해 파일 이름을 지정할 수 없기 때문입니다. 보다 일반적인 해결책이 필요합니다.


S.Lott 가 대답 한 것처럼 Django Framework 에서 문자열을 유효한 파일 이름으로 변환하는 방법을 볼 수 있습니다 .

최신 및 업데이트 된 버전은 utils / text.py에 있으며 "get_valid_filename"을 정의합니다.

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

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


유닉스 시스템 이외의 파일 이름에는 실제로 제한이 없습니다.

  • \ 0을 포함하지 않을 수 있습니다
  • /를 포함하지 않을 수 있습니다

다른 모든 것은 공정한 게임입니다.

$ 터치 "
> 심지어 여러 줄
> 하하
> ^ [[31m 빨강 ^ [[0m
> 악 "
$ ls -la 
-rw-r--r-- 0 11 월 17 일 23:39? 심지어 여러 줄? haha ​​?? [31m 빨강? [0m? 사악한
$ ls -lab
-rw-r--r-- 0 11 월 17 일 23:39 \ neven \ 여러 줄 \ nhaha \ n \ 033 [31m \ red \\ 033 [0m \ nevil
$ perl -e '내 $ i (glob (q {./* even *})) {print $ i; } '
./
심지어 여러 줄
 빨간 

예, 방금 ANSI 색상 코드를 파일 이름으로 저장하고 적용했습니다.

엔터테인먼트를 위해 BEL 캐릭터를 디렉토리 이름에 넣고 CD에 넣을 때 나오는 재미를보십시오.)


한 줄로 :

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

'_'문자를 넣어 더 읽기 쉽게 만들 수도 있습니다 (예 : 슬래시를 교체하는 경우).


re.sub () 메소드를 사용하여 "filelike"가 아닌 것을 대체 할 수 있습니다. 그러나 사실상 모든 인물이 유효 할 수 있습니다. 따라서 사전 빌드 된 기능이 없습니다 (믿습니다).

import re

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

/tmp/filename.txt에 파일 핸들이 생깁니다.


>>> 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'

빈 문자열, 특수 파일 이름 ( 'nul', 'con'등)은 처리하지 않습니다.


조심해야하지만. 라틴어 만보고 있다면 인트로에 명확하게 언급되어 있지 않습니다. ASCII 문자로만 소독하면 일부 단어는 의미가 없거나 다른 의미가 될 수 있습니다.

당신이 "Forêt poésie"(삼림시)를 가지고 있다고 상상해 봅시다.

한자를 다루어야한다면 더 나쁘다.

"下 北 沢"시스템은 결국 "---"를 수행 할 수 있습니다. 따라서 파일 만 다루는 경우 파일을 제어하는 ​​일반 체인이라고하거나 문자를 그대로 유지하는 것이 좋습니다. URI의 경우 거의 같습니다.


try / except로 "osopen"을 감싸고 기본 OS가 파일이 유효한지 여부를 정렬하도록 하시겠습니까?

이것은 훨씬 적은 작업처럼 보이고 어떤 OS를 사용하든 유효합니다.


다른 주석이 아직 해결하지 않은 또 다른 문제는 빈 문자열이며, 이는 유효한 파일 이름이 아닙니다. 너무 많은 문자를 제거하여 빈 문자열로 끝날 수도 있습니다.

Windows에서 예약 한 파일 이름과 점 문제는 무엇입니까? "임의의 사용자 입력에서 유효한 파일 이름을 어떻게 정규화합니까?"라는 질문에 대한 가장 안전한 대답은 무엇입니까? "도 귀찮게하지 마십시오": 다른 방법으로 피할 수 있다면 (예 : 데이터베이스에서 정수 기본 키를 파일 이름으로 사용) 그렇게하십시오.

필요한 경우 공백과 '.'을 정말로 허용해야합니다. 이름의 일부인 파일 확장자의 경우 다음과 같이 시도하십시오.

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

RISC OS는 공백을 싫어하고 '.'를 사용하는 등 예기치 않은 OS에서 특히이를 보장 할 수는 없습니다. 디렉토리 구분자로.


나는 python-slugify 접근법을 좋아했지만 도트를 벗겨 내고 싶지 않았습니다. 그래서 깨끗한 파일 이름을 s3에 다음과 같이 업로드하도록 최적화했습니다.

pip install python-slugify

예제 코드 :

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

산출:

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

이것은 안전 장치이므로 확장명이없는 파일 이름으로 작동하며 안전하지 않은 문자 파일 이름에서만 작동합니다 (결과는 none여기에 있음).


이러한 솔루션의 대부분은 작동하지 않습니다.

'/ hello / world'-> 'helloworld'

'/ helloworld'/-> 'helloworld'

이것은 일반적으로 원하는 것이 아닙니다. 각 링크의 HTML을 저장한다고 가정하면 다른 웹 페이지의 HTML을 덮어 쓰게됩니다.

나는 다음과 같은 구술을 피클한다

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

2는 다음 파일 이름에 추가해야하는 숫자를 나타냅니다.

dict에서 매번 파일 이름을 찾습니다. 존재하지 않으면 필요한 경우 최대 수를 추가하여 새 것을 만듭니다.


OP가 요구 한 것이 아니라 이것이 독특하고 가역적 인 변환이 필요하기 때문에 내가 사용하는 것입니다.

# 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+-_ .'))

최소한 sysadmin의 관점에서는 결과를 "약간"읽을 수 있습니다.


나는 이것이 반복되는 문자열을 수정하기 때문에 큰 대답이 아니라고 확신하지만 잘 작동하는 것 같습니다.

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, '')

최신 정보

이 6 살짜리 답변에서 수리를 넘어 모든 링크가 끊어졌습니다.

또한 더 이상이 방법을 사용하지 않고 base64안전하지 않은 문자를 인코딩하거나 삭제하십시오. 파이썬 3 예제 :

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

함께 base64사용하면 인코딩 및 디코딩 할 수 있습니다, 그래서 당신은 다시 원래의 파일 이름을 검색 할 수 있습니다.

그러나 사용 사례에 따라 임의의 파일 이름을 생성하고 메타 데이터를 별도의 파일 또는 DB에 저장하는 것이 좋습니다.

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'

원래 링크 로트 답변 :

bobcat프로젝트에는 작업을 수행하는 Python 모듈이 포함되어 있습니다.

완전히 견고하지는 않습니다 . 게시물 과이 회신을 참조하십시오 .

따라서 언급했듯이 : base64가독성이 중요하지 않으면 인코딩이 더 나은 아이디어 일 것입니다.


나는 많은 답변이 있다는 것을 알고 있지만 대부분 정규 표현식이나 외부 모듈에 의존하므로 내 자신의 답변을 던지고 싶습니다. 순수한 파이썬 함수, 외부 모듈 필요 없음, 정규 표현식 사용 없음. 내 접근 방식은 유효하지 않은 문자를 지우는 것이 아니라 유효한 문자 만 허용하는 것입니다.

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    

원하는 경우 validchars영어 알파벳이없는 자국 문자와 같이 시작 부분에 고유 한 유효한 문자를 변수에 추가 할 수 있습니다 . UTF-8에서 실행되지 않는 일부 파일 시스템은 ASCII가 아닌 문자에 여전히 문제가있을 수 있습니다.

이 함수는 단일 파일 이름 유효성을 테스트하기 위해 유효하지 않은 문자를 고려하여 경로 구분 기호를 _로 바꿉니다. 이를 추가 if하려면 os 경로 구분 기호를 포함 하도록 수정하는 것이 간단합니다 .


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)

참고 URL : https://stackoverflow.com/questions/295135/turn-a-string-into-a-valid-filename

반응형