본문 바로가기

진리는어디에/Python

파이썬으로 utf-8 문서 읽기

들어가며

위키(웹)에서 작성된 텍스트 파일을 읽어 들여 특정 문자열을 변환 해야 하는 이슈가 있었습니다. 예를 들자면 :

= 제목 =
 * 내용1
 * 내용2

위의 내용중에서 '제목'이라는 문자열을 찾아 모두 '타이틀'로 변경 하는 작업이 필요하다고 생각하시면 됩니다.

변환 하는 방법이 그렇게 어렵지는 않을 것입니다. 간단히 생각을 해보면 :

  1. 디렉토리를 순회 하며 파일의 이름들을 얻어 온다.
  2. 파일을 열어 특정 문자열을 찾아, 변경 한다.
  3. 파일을 저장하고 다른 파일들에 대해 2번 과정을 적용한다.

그렇게 문자열을 처리하는데 편리한 기능들을 많이 제공하는 언어를 사용한다면 그렇게 어려운 일도 아닐 것 입니다. 파이썬을 이용하자면 아래와 같은 코드가 나올 수 있겠지요 :

import os
for root, dirs, files in os.walk('.') :
    for file in files :
        fp = open(file,'r')
        result = ''
        try :
            for line in fp :
                line = line.replace("변환대상문자열1", "변환문자열1")
                line = line.replace("변환대상문자열2", "변환문자열2")
                line = line.replace("변환대상문자열3", "변환문자열3")
                line = line.replace("변환대상문자열4", "변환문자열4")
                # 기타등등 문자열 변환
                result += line
        finally :
            fp.close()

        fp = open(file, 'w')
        fp.write(result)
        fp.close()

※ 위 코드는 변환 프로그램 자신도 변환 대상으로 바라 볼 수 있습니다. 때문에 한번 프로그램을 돌리고 나면 파이썬 코드도 변경해버려 두번 이상 돌리는 것은 불가능 합니다. 디렉토리를 다른 곳으로 지정하든 파이썬 파일은 무시하든 처리가 필요 합니다만, 설명을 위한 예제이므로 위와 같은 코드가 있다는 개념 정도만 알아 주시면 됩니다.

하지만 이 프로그램이 제대로 돌아갈까요? 제대로 돌아 갔었다면 제가 이 포스팅을 하고 있지도 않았겠지요. 물론 여러분의 기대와 같이 제대로 동작하지 않았습니다. 무엇이 문제 였을 까요?

바로 한글!!! 한글을 처리하는 인코딩에 관련된 문제 였습니다. 웹에서 작성된 텍스트 파일의 인코딩은 UTF-8이고 제가 작성한 파이썬 파일은 ASCII였던 것이지요.

UTF-8은 가변적인 길이를 가지는 인코딩 방법인지라 영어에서는 별 문제가 없지만 한글에서는 다른 인코딩 방법과 마찰을 일으킨답니다. 예를 들자면 유니코드의 '금'이라는 한글은 '0xAE08'입니다. 이진수로 변환해 보자면 '1010 1110 0000 1000'입니다. 하지만 이것이 UTF-8로 변환되면 '1110 1001 1011 1000 1000 1000'이라는 보다 더 긴 바이트를 가지게 되는겁니다. 컴퓨터가 보는 문자열은 당연히 치환하려는 문자열과 제가 파이썬 코드에서 작성한 문자열은 같지 않기 때문에 치환하고 싶은 문자열을 하나도 찾지 못 하겠다고 시치미를 뗄 뿐입니다.

UTF-8 문서 읽기

위 예제의 4 라인의 open을 아래와 같이 변경 해주면 UTF-8 인코딩으로 파일을 읽을 수 있습니다.

fp = open(file, "r", encoding='UTF8')

인코딩 포멧 알아 오기

자, 그럼 파일 포멧이 달랐다는 것을 어떻게 알았을까요? 변환 대상 문서를 열어 보니 :

吏€湲덇퉴吏€ vsftpd???먯껜 蹂댁븞 臾몄젣媛€ ?덉뼱 蹂댁븞沅뚭퀬媛€ ?섏삩 ?곸씠 ?놁쓣 ?뺣룄濡?蹂댁븞?깆씠 ?곗뼱?섎떎.[[BR]]

----
=== 湲곕낯 ?뺣낫 ===
 * OS : Red Hat Linux release 7.3
 * ?⑸룄 : GP 踰꾩텛???ъ씠?몄슜 ?쒕쾭 諛?媛쒕컻 ?뚯뒪?몄슜 ?ㅼ슫濡쒕뱶 ?ъ씠??

=== 二쇱슂 湲곕뒫 ===
- 媛€??IP蹂?蹂꾨룄???섍꼍 ?ㅼ젙 湲곕뒫 (?ㅼ젙?뚯씪??listen_address= ?댁슜) [[BR]]
- 媛€???ъ슜???ㅼ젙[[BR]]
- ?꾩넚 ?€??룺 吏€??[BR]]
- PAM 吏€??(踰꾩쟾 1.2.0遺€?곕뒗 PAM???듯븳 wtmp??濡쒓릿 濡쒓렇瑜??④?)[[BR]]
- xferlog ?쒖? 濡쒓렇 ?뚯씪蹂대떎 ?곸꽭???먯껜 濡쒓렇 ?뚯씪 ?뺤떇 吏€??[BR]]
- Standalone 諛⑹떇怨?inetd(xinetd)瑜??듯븳 ?댁쁺 紐⑤몢 吏€??[BR]]
- IP蹂??ㅻⅨ ?섍꼍 ?뚯씪 吏€??湲곕뒫 (tcp_wrappers?€ ?④퍡 ?ъ슜????[[BR]]

위와 같이 나옵니다. 딱봐도 인코딩이 깨진 것이 보이지요. 영어는 잘 보이지만, 한글 부분은 깨졌잖아요. vi가 기본적으로 보여 주는 인코딩 셋은 아마도 ASCII일 것입니다. 그렇다면 이 파일의 인코딩 셋은 무엇일까요? 리눅스에서는 'file'이라는 특정 파일의 정보를 얻어 내는데 도움이 되는 명령어를 제공하고 있습니다. 특히 인코딩 정보를 말입니다 :

[kukuta@server ~]$ file wikifile
wikifile: UTF-8 Unicode text
[kukuta@server ~]$ file pythonfile.py
pythonfile.py: ASCII Java program text

위의 두 파일의 인코딩 정보가 다르지요?

  • 인코딩 셋이 맞지 않는 것 같다라고 생각이 되시면 언제든지 'file'을 이용하세요

파일 인코딩 변경하기

이제 문제를 해결 하는 것에 초점을 맞춰 보겠습니다. 지금 당장 우리에게 필요 한 것은 어떤 인코딩 방법으로 파일을 읽어들이느냐가 아닙니다.

어떻게 해서든지 파이썬 코드에서 작성된 '변환대상문자열1'이 웹에서 작성된 '변환대상문자열1'과 같다라는 것을 컴퓨터에게 알려야만 합니다

. 크게 두 가지 방법이 있는데 한 가지 방법은 파이썬 버젼의 제약을 받는 것이고, 나머지 한 가지는 제가 아는 한도 내에서는 리눅스에서만 할 수 있는 것입니다(물론 윈도우에서도 할 수 있겠지만 제가 방법을 모릅니다).

첫번째 방법은 비교할 문자열을 'utf-8'로 인코딩 하여 비교하는 것입니다. 'How to use UTF-8 with Python'이라는 글에 잘 나와있습니다만, 저는 저 방법을 이용해서는 잘 안되더군요.

두 번째 방법은 파이썬 파일의 인코딩 셋을 utf-8로 변경 해 버리리는 것입니다. 만일 제가 '변환대상문자열1'이라는 문자열을 파이썬에 쓰고, 저장을 하게 된다면 단순 유니코드로 저장이 되겠지요. 하지만 이 파일의 인코딩 셋을 utf-8로 변경하게 된다면 우리가 변경하고자 하는 웹에서 작성된 텍스트 파일에 있는 문자열과 동일한 바이트를 가진 문자열로 변환 되는것 입니다. 리눅스에서는 파일의 인코딩 셋을 변경하기 위해 'iconv'라는 커맨드를 제공 하고 있습니다 :

[kukuta@server ~]$ iconv -f euc-kr -t utf-8 pythonfile.py --output pythonfile.py

위와 같이 하고 나면 유니코드로 작성되어 있던 한글이 utf-8로 변환 됩니다. 아까 제대로 보이던 한글은 이제는 깨져서 보일 것입니다만, 중요한 것은 컴퓨터가 변환하고자 하는 한글과 우리가 적은 한글이 동일함을 이제 부터는 안다는 것이지요.

부록 1. 같이 읽으면 좋은 글

유익한 글이었다면 공감(❤) 버튼 꾹!! 추가 문의 사항은 댓글로!!