인프런 커뮤니티 질문&답변

천승업님의 프로필 이미지

작성한 질문수

남박사의 파이썬으로 실전 웹사이트 만들기

글 작성시 첨부파일 기능 구현하기

파일이름 체크하는 부분 질문드립니다.

작성

·

386

1

def check_filename(filename):
    reg = re.compile("[^A-Za-z0-9_.가-힝-]")
    for s in os.path.sep, os.path.altsep:
        if s:
            filename = filename.replace(s, ' ')
            filename = str(reg.sub('', '_'.join(filename.split()))).strip("._")
    return filename

부분인데요

만약 파일이름을 ../../filename/.bash  로 입력한다면

파일이름은 filename_.bash로 나와야 하는거 아닌가요?

실제로 실행해보면 filename.bash로나오긴하는데
strip 함수가 왼쪽 오른쪽에서 ._을 제거하지만
중간에 있는 _는 제거하지 못하는 거잖아요?

하나씩 해보자면
1번 .. .. filename .bash   
2번 .._.._filename_.bash
3번 filename_.bash  

이렇게 3번으로 파일이름이 나오는 것 같은데 
실제로 리턴값 출력하면 filename.bash 이렇게 나옵니다.
filename_.bash 의 _ 를 어떻게 제거하는지 궁금하네요.



답변 부탁드립니다.

답변 5

0

천승업님의 프로필 이미지
천승업
질문자

마지막 질문입니다...
윈도우에서 실행하였습니다.

import re
import os

def check_filename(filename):
    reg = re.compile("[^A-Za-z0-9_.가-힝-]")
    for s in os.path.sep, os.path.altsep:
        if s: 
            filename1 = filename.replace(s, ' ')
            print("filename =>   " + filename1)
            join = '_'.join(filename1.split())
            print("join  =>   " + join)
            sub = reg.sub('', join)
            print("sub  =>   " + sub)
            strip1  = str(sub).strip("._")
            print("strip1   =>   " + strip1 )
            filename = strip1
            
    return filename

print(check_filename("../../filename/.bash"))

12412.png

filename = strip1 값을 마지막줄에 넣어서 정상적으로 파일네임이 나오는 것이 확인됩니다.

여기서 궁금한점이 결과출력 1번에 보면 그것은

filename.replace(s, ' ') 의 결과물 값을 의미하는데

그것은 슬러시나 역슬러시 문자를 공백으로 만든다는 의미잖아요
그런데 결과출력 1은 공백으로 나오지 않은것인지 하는 부분이요...

제 생각에 결과출력 1은  

filename  =>  .. .. filename .bash

이렇게 나와야 할거같은데 말이죠..

그리고 결과출력 2번도 공백을 _ 으로 묶는다는 의미니까

join => .._.._filename_.bash

이렇게 나와야 할 거 같은데 이렇게 나오질 않네요..
남박사님의 프로필 이미지
남박사
지식공유자

def check_filename(filename):
    reg = re.compile("[^A-Za-z0-9_.가-힝-]")
    for s in os.path.sep, os.path.altsep:
        if s: 
            print(f's={s}')

에서 처럼 단지 s 값을 화면에 출력해보면 윈도우의 경우 \ 역슬러시를 처리합니다. 그런데 주어진 조건은 그냥 / 슬러시입니다.

filename => ../../filename/.bash

join => ../../filename/.bash

그러니 당연히 위의 filename은 replace를 거쳤다 하더라도 위와 같이 나오는게 정상입니다. 역슬러시를 치환하는데 슬러시 문자가 바뀌면 안되겠죠.

지나가다가 우연히 보게되어 답변 드려요

질문하신 사항이 저도 순간 , 맞네 이상하네라고 생각했는데

실행해보니, 그게 아니었네요

 

질문의 요지는

.. .. filename .bash 이게

.._.._filename_.bash 이렇게 바뀌고

중간에 있는 _. 은 split으로 지울수 없다 라는거 같네요

 

print하면서 실행해 보니

.. .. filename .bash > .._.._filename_.bash

이렇게 한번에 바뀌는게 아니고

for 문 한번 돌때

.. .. filename .bash > ['../../filename/.bash'] > ../../filename/.bash > ....filename.bash

이렇게 바뀜니다 그래서

filename 은 ....filename.bash 이게 되고

다시 for문을 돌아서 spilt에 의해서

filename.bash 이게 됩니다.

 

(혹시 그럼 "/" 이게 아니고, "\" 이거일때는 안되지 않냐라고 생각하실텐데, for문 두번도는 로직에 의해 잘됩니다)

제가 작성한코드입니다, 질문자님가 똑같은 코드인데 조금더 설명을 붙였습니다

def check_filename(filename):
    reg = re.compile("[^a-zA-Z_.가-힝-]")   # 문자가 아닌걸 담음
    count = 1
    for s in os.path.sep, os.path.altsep:
        if s:   # 무조건 하는데, 두번하네
            print('----- for 문 {}번째 -----'.format(count))
            filename = filename.replace(s, ' ')
            print("replace:",filename)
            filename = filename.split() # 리스트 만듬
            print("split:",filename)            
            filename = '_'.join(filename)   # _ 조인 : ex) a_b_c
            print("join:",filename)            
            filename = reg.sub('', filename)   
            print("정규식적용:",filename)            

            print("*" * 30)
            count += 1
    return filename

check_filename("../../filename/.bash")

 

참고로 출력 print는 아래와 같습니다

----- for 문 1번째 -----

replace: .. ..

join: ../../filename/.bash

정규식적용: ....filename.bash

******************************

----- for 문 2번째 -----

replace: ....filename.bash

split: ['....filename.bash']

join: ....filename.bash

정규식적용: ....filename.bash

******************************

강의를 5번 정도째 보고있는데 이제야 이해가 가네요

질문 작성자님도 화이팅하세요

남박사님강의가 진짜 진국입니다.

 

0

남박사님의 프로필 이미지
남박사
지식공유자

테스트를 잘못 하고 계십니다. filename = strip1 을 제거하면 전혀 의도하지 않은 상태로 동작하게 됩니다. filename = strip1 을 적용하지 않으면

filename => ../../filename/.bash

filename => .. .. filename .bash

for문에 의해 filename의 값은 위의 2가지 경우를 처리하게 됩니다. 첫번째 filename 값은 최초 함수 실행시의 값이고 두번째 filename은 첫번째 반복문에 의해 결정된 값입니다. 여기서 두번째 filename 값이 첫번째 strip1에 의한 값이 아니기 때문제 전제 자체가 잘못된 전제조건이 됩니다.

만약 filename = strip1을 적용했더라면

filename => ../../filename/.bash

filename => filename.bash

filename의 값은 최초 함수 호출시 넘어온 값과 첫번째 for문을 수행한 결과의 filename 값이 되므로 위와 같습니다. 여기서보면 이미 첫번째 for문에 의해 filename은 최종 결과와 같은 상태로 변경되어있는데 첫번째 for문에서 일어난 과정을 보면

filename => ../../filename/.bash

join => ../../filename/.bash

sub => ....filename.bash

strip1 => filename.bash

filename => filename.bash

위와 같이 처리됨을 확인할 수 있습니다. 결론적으로 윈도우에서는 for문이 2회 반복되고 리눅스에서는 1회 반복되며 말씀하신 중간에 언더스코어가 사라지는 경우는 존재하지 않습니다. filename = strip1 을 제거하고 테스트를 했을때 결과가 그리 보이는건

filename => ../../filename/.bash

join => ../../filename/.bash

sub => ....filename.bash

strip1 => filename.bash

filename => .. .. filename .bash

join => .._.._filename_.bash

sub => .._.._filename_.bash

strip1 => filename_.bash

최종: ../../filename/.bash

../../filename/.bash

위의 과정에서 그냥 처음에 함수 호출시 전달한 filename값이 반환되었기 때문입니다. stirp1과 아무런 연관이 없고 함수내의 어떤 동작결과와도 아무런 상관이 없는 값입니다.

0

천승업님의 프로필 이미지
천승업
질문자

윈도우 운영체제에서 실행하였습니다.

import re
import os

def check_filename(filename):
    reg = re.compile("[^A-Za-z0-9_.가-힝-]")
    for s in os.path.sep, os.path.altsep:
        if s: 
            filename1 = filename.replace(s, ' ')
            print("filename =>   " + filename1)
            join = '_'.join(filename1.split())
            print("join  =>   " + join)
            sub = reg.sub('', join)
            print("sub  =>   " + sub)
            strip1  = str(sub).strip("._")
            print("strip1   =>   " + strip1 )
            
            
    return filename

print(check_filename("../../filename/.bash"))

12.png

print("strip1   =>   " + strip1 ) 코드 아랫줄에

filename = strip1 를 넣어줘야 제대로 작동하는데요
일부러 그 부분 제외하고 실행시켜 보았습니다.

그러면 strip1 값이 filename_.bash 로 나옵니다.

그다음에 filename = strip1 를 넣어서 실행하면
정상적으로 filename.bash 로 나옵니다.

어쨋든 저기서 strip1 값은 filename_.bash입니다.
그런데 strip1 에서 갑자기 중간에 있는 _ 가 사라지는지가
궁금할 따름입니다.

0

천승업님의 프로필 이미지
천승업
질문자

윈도우에서 실행했고

filename.bash로 잘 나옵니다.

strip 함수가 중간의 _를 제거하지는 못할텐데

리턴하면 희한하게 제거가 자동적으로 되네요

남박사님의 프로필 이미지
남박사
지식공유자

어떤 부분이 희안하게 느껴지시는지 잘 모르겠습니다만...

import os
import re
def check_filename(filename):
    reg = re.compile("[^A-Za-z0-9_.가-힝-]")
    print(os.path.sep, os.path.altsep)
    for s in os.path.sep, os.path.altsep:
        if s:
            print('S >>>>' + filename)
            filename = filename.replace(s, ' ')
            print('R >>>>' + filename)
            join = '_'.join(filename.split())
            print('J >>>' + join)
            sub = reg.sub('', join)
            print('U >>>>' + sub)
            filename = str(sub).strip("._")
            print('A >>>>' + filename)
    return filename

a = check_filename("../../filename/.bash")
print(a)

위의 코드처럼 코드를 줄여쓰지 않고 그냥 있는 그대로 작성해서 과정을 살펴보시면 어떤 일이 일어나고 있는지 확인할 수 있습니다.

image

실행하면 위의 과정을 거쳐서 결과가 만들어지는걸 확인할 수 있습니다. 여기서 어떤 구간이 희안하게 느껴지시는지를 제가 잘 모르겠습니다.

0

남박사님의 프로필 이미지
남박사
지식공유자

그게 보시면...

운영체제가 리눅스냐, 윈도우냐에 따라 달라집니다.

image

위는 윈도우에서 실행했을때..

image

위는 리눅스에서 실행했을때..

보시면 os.path.sep, os.path.altsep 값이 다릅니다. 이부분을 확인해보시기 바랍니다.