[출처 : http://www.daniweb.com/code/snippet216704.html]


# experiment with wxPython's
# wx.media.MediaCtrl(parent, id, pos, size, style, backend)
# the backend (szBackend) is usually figured by the control
# wxMEDIABACKEND_DIRECTSHOW   Windows
# wxMEDIABACKEND_QUICKTIME    Mac OS X
# wxMEDIABACKEND_GSTREAMER    Linux (?)
# plays files with extension .mid .mp3 .wav .au .avi .mpg
# tested with Python24 and wxPython26 on Windows XP   vegaseat  10mar2006

import wx
import wx.media
import os

class Panel1(wx.Panel):
    def __init__(self, parent, id):
        #self.log = log
        wx.Panel.__init__(self, parent, -1, style=wx.TAB_TRAVERSAL|wx.CLIP_CHILDREN)

        # Create some controls
        try:
            self.mc = wx.media.MediaCtrl(self, style=wx.SIMPLE_BORDER)
        except NotImplementedError:
            self.Destroy()
            raise

        loadButton = wx.Button(self, -1, "Load File")
        self.Bind(wx.EVT_BUTTON, self.onLoadFile, loadButton)
        
        playButton = wx.Button(self, -1, "Play")
        self.Bind(wx.EVT_BUTTON, self.onPlay, playButton)
        
        pauseButton = wx.Button(self, -1, "Pause")
        self.Bind(wx.EVT_BUTTON, self.onPause, pauseButton)
        
        stopButton = wx.Button(self, -1, "Stop")
        self.Bind(wx.EVT_BUTTON, self.onStop, stopButton)

        slider = wx.Slider(self, -1, 0, 0, 0, size=wx.Size(300, -1))
        self.slider = slider
        self.Bind(wx.EVT_SLIDER, self.onSeek, slider)
        
        self.st_file = wx.StaticText(self, -1, ".mid .mp3 .wav .au .avi .mpg", size=(200,-1))
        self.st_size = wx.StaticText(self, -1, size=(100,-1))
        self.st_len  = wx.StaticText(self, -1, size=(100,-1))
        self.st_pos  = wx.StaticText(self, -1, size=(100,-1))
        
        # setup the button/label layout using a sizer
        sizer = wx.GridBagSizer(5,5)
        sizer.Add(loadButton, (1,1))
        sizer.Add(playButton, (2,1))
        sizer.Add(pauseButton, (3,1))
        sizer.Add(stopButton, (4,1))
        sizer.Add(self.st_file, (1, 2))
        sizer.Add(self.st_size, (2, 2))
        sizer.Add(self.st_len,  (3, 2))
        sizer.Add(self.st_pos,  (4, 2))
        sizer.Add(self.mc, (5,1), span=(5,1))  # for .avi .mpg video files
        self.SetSizer(sizer)

        self.timer = wx.Timer(self)
        self.Bind(wx.EVT_TIMER, self.onTimer)
        self.timer.Start(100)
        
    def onLoadFile(self, evt):
        dlg = wx.FileDialog(self, message="Choose a media file",
                            defaultDir=os.getcwd(), defaultFile="",
                            style=wx.OPEN | wx.CHANGE_DIR )
        if dlg.ShowModal() == wx.ID_OK:
            path = dlg.GetPath()
            self.doLoadFile(path)
        dlg.Destroy()
        
    def doLoadFile(self, path):
        if not self.mc.Load(path):
            wx.MessageBox("Unable to load %s: Unsupported format?" % path, "ERROR", wx.ICON_ERROR | wx.OK)
        else:
            folder, filename = os.path.split(path)
            self.st_file.SetLabel('%s' % filename)
            self.mc.SetBestFittingSize()
            self.GetSizer().Layout()
            self.slider.SetRange(0, self.mc.Length())
            self.mc.Play()
        
    def onPlay(self, evt):
        self.mc.Play()
    
    def onPause(self, evt):
        self.mc.Pause()
    
    def onStop(self, evt):
        self.mc.Stop()
    
    def onSeek(self, evt):
        offset = self.slider.GetValue()
        self.mc.Seek(offset)

    def onTimer(self, evt):
        offset = self.mc.Tell()
        self.slider.SetValue(offset)
        self.st_size.SetLabel('size: %s ms' % self.mc.Length())
        self.st_len.SetLabel('( %d seconds )' % (self.mc.Length()/1000))
        self.st_pos.SetLabel('position: %d ms' % offset)


app = wx.PySimpleApp()
# create a window/frame, no parent, -1 is default ID
frame = wx.Frame(None, -1, "play audio and video files", size = (320, 350))
# call the derived class
Panel1(frame, -1)
frame.Show(1)
app.MainLoop()
[출처 : http://forcecore.tistory.com/1033]


wxGlade가 처음에는 구려보이지만, 있는 것들 중 가장 나은것이라는 점을 알 수 있다 -_-;; 해보면...;; 주의할 점은, 한글 지원이 안 된다는 것이다. 아니면 필자가 못하는건가? 처음에 디자인 단계는 모든 것을 영어로 한 뒤, 나중에 코드생성이 된 이후, 코드를 한글로 좀 고쳐주면, python자체는 한글이 안 되는건 아니라 그럭저럭 한글이 되긴 한다.

우선 wxGlade의 리소스 파일이 저장될 곳을 물색해야 한다.
주의할 점은, 저장되는 곳은 절대로 한글이 들어가지 않는 폴더여야 한다는 점이다. 그런 측면에서, 윈도우 사용자 이름이 한글이거나, 바탕화면, 내 문서 같은 곳은 고르면 안 된다 -_-;;

필자는 c:\mod 폴더를 생성했다.

이제 wxGlade를 실행시킨다.


이런 썰렁한 창이 뜬다. 뭘 하란거지? 처음엔 좀 황당하다.

알아두어야 할 것이, 델파이나 비주얼 스튜디오와는 달리, 거의 모든 요소들이 boxSizer라는 것 안에 들어가야 한다는 점이다.

처음에는 wxGlade:Tree 라고 적힌 창에 Application만 있어야 한다.
팔레트 창에서 add a dialog버튼을 누르면 창을 하나 볼 수 있게 된다.


이렇게 되어 있는데.. class는 적당히 MainDialog 이런 식으로 적고 OK를 누른다. wsDialog를 선택하는건 필수;


아무것도 없는 창이 하나 더 보이게 될 것이다. 여기다가 대고 디자인을 하면 되는데, 버튼을 두개 추가하려고 해도 잘... 안 될 것이다. 이유는... 아까도 말 했지만, BoxSizer라는 것을 이용해야 하기 때문이다. 버튼을 혹시 넣으려고 해봤다면 -_- tree에서 오른 클릭을 해서 지우든, 창에서 선택하고 del버튼을 누르든 선택하여 없애라.

이쯤에서 필자의 최종 목표를 미리 밝혀야 할 듯 싶다.


이런 창을 만드는 것이다. 저 구성을 염두에 두고...

다시 원상 복구가 된 뒤 이번에는, BoxSizer를 넣는다. 그리고 메인창에 클릭을 하면...


이런 창이 뜨는데, vertical을 택하고, slot은 2를 선택한다. 왜 2냐면, 필자는 크게 저 그림을 품는 영역과 버튼을 품는 영역을 나누고 싶기 때문이다. 이런식으로 영역 나눔을 먼저 해야지 그 영역에 버튼을 놓든 말든 할 수 있다는 특징이 있다.


창이 이렇게 될 것이다. 빗금친 영역에는 버튼과 같은 것을 놓을 수 있다.

Static Bitmap을 아래에다 놓고, 그림을 불러오자. StaticBitmap버튼을 클릭한뒤 아래 빗금에 클릭하면, 원하는 이미지를 불러올 수 있게 된다.


두둥. 저 창을 닫았다가 다시 켜보자. 닫은 다음에 다시 켜려면, tree에서 dialog에 더블클릭을 하면 된다.


쪼그라들었다 ㅡ,.ㅡ;; 보통은 wxWidget의 (wxPython이 사용하는 라이브러리!) 구성요소들은, 사이즈가 좀 지멋대로다. 일부러 지정해주지 않는한은...

이제쯤 저장하라. 언두 기능이 없어서, 뭔가 잘못해서 큰 덩어리 하나를 실수로라도 삭제하면, 이제껏 한 작업이 통째로 날아가게 된다 -_-;;;;;; 버전 0.6.3 기준임. 버전업되면 생기겠지... OTL

이제 BoxSizer로 6칸짜리 vertical을 윗 영역에 넣자. 창이 너무 쪼그라 들어서, 제대로 안 보일 것이다. 창을 껐다 다시 켠다.


그러면 이렇게 보인다. 아래 다섯칸에는 버튼을 넣는다.


... 문제가 두개 있다.
1. 버튼 가로길이가 너무 작다 -_-
2. 버튼에 여백이 없어서, 창에 찰싹 달라 붙어 있다.


키우고자 하는 버튼을 선택하고, properties창의 layout탭에서 wxEXPAND를 해주면, 늘어날 수 있는 한도까지 쭉 늘어난다.


그 외에, size에 체크박스를 하고 일부러 크기를 입력해주면 크기를 정확하게 넣을 수 있다. 하지만 필자의 의도는, 로고 그림이 커지면 버튼도 같이 커지는 것이므로, 위에 설명한 wxEXPAND면 충분하다. 버튼의 세로 사이즈가 -1로 되어있는데, 이는, 그냥 default치를 사용하는 것이다?

Properties에서 Widget탭을 보면, 버튼에 어떤 글자가 들어가야 하는지 지정해 줄 수 있다. Common의 name항목도 예쁘게 해주어야 한다. 가령, Quit이라고 적힌 버튼은, name이 button_quit 으로 필자가 설정했다.

이제 여백도 넣어볼까... 두번째에 넣었던 버튼용 BoxSizer의 property에서


이렇게 Border를 0이 아닌 값으로 주고, Border중, 여백을 줄 방향에 체크해주면 됨.

이제 enable, disable버튼 두개를 추가해주고 싶은데... 빈 영역에 horizontal BoxSizer를 넣자. 슬롯 두개짜리.


이 버튼들 가로 크기가 동일하고, 가로로 꽉 채웠으면 좋겠는데... 역시 죽어도 안 된다. 오히려, wxEXPAND를 사용하면 세로로는 늘어나는 것을 볼 수 있다. 그 이유는... BoxSizer가 Horizontal이면 버튼은 세로로 expand되고, vertical이면 가로로 expand되기 때문이다. -_-^;; 어쩔 수 없이... 머리를 좀 써서... 버튼 두개만 삭제하고, 버튼이 있던 자리에, 크기1짜리의 vertical BoxSizer를 넣는다 -_-;; 그러면 된다.


모양은 완성됐다. 두둥.

이제 Quit버튼에 들어갈 이벤트 핸들러 (클릭시 일어나면 프로그램이 취할 행동) 를 넣어보자.
Quit버튼을 선택하고 Events의 EVT_BUTTON에
 

on_btn_quit 과 같은 것을 넣어주자. 마우스 오버같은 이벤트는 디텍트 안 되나? 하긴 그런 프로그램은, "보통은" 나오지 않는... 데...;; 예쁜 프로그램을 만들거 아니면.

이제 모양은 다 디자인 했으니 python코드를 생성하라고 해보자.


트리에서, application을 선택하면, 프로퍼티 창에 저런 게 뜬다. Encoding이 좀 기분나쁜 값으로 되어 있는데, 현재로서는 무시하자. 어차피 우리가 코딩을 좀 하면서 수정할 것이다. 그리고 옵션은 원하는 대로 선택하고, Output path의 ...버튼을 클릭하여, 생성될 코드의 이름을 지정해주자. Generate Code버튼을 클릭하면 코드가 나오고...



코드를 실행하면 원하는대로 창이 뜬다. wxGlade에서 보던 것 보다 버튼같은게 테마 적용이 되어 예쁘게 보인다. Quit버튼을 클릭하면, on_btn_quit이 아직 구현되지 않았다고 뜰 것이다.

아까 생성한 스크립트를 이제 텍스트 편집기로 열어보자.

파일 시작 부분에는
#!/usr/bin/env python
# -*- coding: iso-8859-15 -*-
# generated by wxGlade 0.6.3 on Tue Jun 30 00:20:50 2009

import wx

이런게 있는데 iso어쩌구 대신 cp949를 넣어주자. wsGlade에서 하지 않은 이유는? cp949라고하면, "나는 그런 인코딩은 몰라"라고 wxGlade가 우기기 때문이다 -_-;;

코드를 더 읽다보면
        self.button_en = wx.Button(self, -1, "Enable")
        self.button_dis = wx.Button(self, -1, "Disable")
        self.button_run = wx.Button(self, -1, "Run")
        self.button_modhome = wx.Button(self, -1, "Mod Homepage")
        self.button_red2net = wx.Button(self, -1, "red2.net")
        self.button_quit = wx.Button(self, -1, "Quit" )
이런 버튼에 적힐 글귀가 있는 코드가 있는데 그것도 고쳐주자.


한글도 잘만 된다. +_+

    def on_btn_quit(self, event): # wxGlade: MainDialog.<event_handler>
        print "Event handler `on_btn_quit' not implemented"
        event.Skip()

quit버튼을 누르면 구현이 안 됐다고 뜨게 하는 기능은 여기 있다. 여기있는 place holder 코드를 지우고 원하는 기능으로 고친다.

    def on_btn_quit(self, event): # wxGlade: MainDialog.<event_handler>
        self.Close()

창을 닫는 코드를 실제로 넣어 주었다.

다시 실행을 시켜보면, 창은 닫히는데, 검은창은 없어지지 않는다... 원래 그런거지...
필자가 스크립트의 이름을 mod.py 라고 지었기 때문이다.
mod.pyw로 이름을 변경하라. 그러면 땡이다.

다음 시간에는 exe로 컴파일도 해보자.
출처 : http://forcecore.tistory.com/1032]


Python 2.6기준으로 쓴 글임을 밝힌다. 주요 사용자 계층은 32비트 윈도우 사용자다. 자바는 자바를 따로 설치해야 해서 사용자들의 귀차니즘이 있는고로, python을 선택함.

환경 갖추기:
0. vim이 되었든 뭐가 되었든 좋아하는 에디터가 python 하이라이팅을 지원하게 만든다.

이제부터 프로그램을 여러개 설치하게 되는데... 다 넥스트 넥스트 눌러서 설치하면 된다. 참 쉽죠?

1. python 윈도우용을 받는다.
http://www.python.org/download/
여기에서, 필자는 2.6.2 windows installer를 받아서 설치했다.
python-2.6.2.msi 이런거

2. wxPython도 받아서 설치해야지 GUI 지원이 된다?
http://www.wxpython.org/download.php
여기서 python2.6이라고 된 것에서 win32-unicode를 받는다.

wxPython2.8-win32-unicode-2.8.10.1-py26.exe
이렇게 생겼음.

3. GUI프로그램을 GUI적이지 못하게 코딩으로 만들려면 인생이 고달프므로... 코드를 어느정도 자동으로 생성해주는 프로그램이 필요하다. 필자는 PythonCard, Boa Constructor, wxGlade 세가지를 써봤는데, 가장 나은 것은 wxGlade이다.

wxGlade는 http://wxglade.sourceforge.net/ 여기 있다. 아니, 좀 더 정확히는
http://sourceforge.net/projects/wxglade 여기.
wxGlade-0.6.3-setup.exe 이렇게 생긴 것을 받는다.

4. 마지막으로 python으로 짠 것을 exe로 실행시킬 수 있게 해야 한다.
http://www.py2exe.org/ 여기서
py2exe-0.6.9.win32-py2.6.exe
이런 것을 받는다. 2.6으로, 자신의 python 버전과 맞는 것을 설치해야지 됨.
[출처 : http://www.imp17.com/tc/myevan/95]


일단 리눅스의 사운드 연주 소스입니다.

#!/usr/bin/python
import linuxaudiodev
import wave

src = wave.open("test.wav""rb")
channels, bytes, freq, frameCount, compType, compName  = src.getparams()

dsp = linuxaudiodev.open("/dev/dsp""w")
dsp.setparameters(freq, bytes*8, channels, linuxaudiodev.AFMT_S16_LE, False)

frameStep = 1000
for pos in range(0, frameCount, frameStep):
    buf = src.readframes(frameStep)
    dsp.write(buf)

리눅스에서 사운드 플레이를 정말 쉽습니다 -_-)~ 그냥 모든게 파일이라 좋아요.

하지만 역발상의 대가 윈도우에서는 유닉스와는 다른길을 걷기 때문에
위의 코드로 작동이 되지 않습니다 -_-)> wav 파일이라면 winsound 라는 모듈을 지원하지만
지금 작업은 RAW 데이터를 플레이하는거라서 직접 wave파일에 접근해야합니다.

다행히 http://www.johnnypops.demon.co.uk/ 라는 사이트에서 
녹음 기능을 ctypes 로 구현해놓은것이 있어서 (정말 좋은 사람이예요 >ㅁ<)/ )
이 소스를 활용해 같은 기능을 하는 프로그램을 만들어보았습니다.
(c++ 객체만 ctypes 로 꺼낼수 있으면 정말 행복할거예요)
[출처 : http://www.imp17.com/tc/myevan/72?category=4]


dir 만으로는 원하는 함수를 찾기 힘들때가 있다.

def candi(obj, findName):
    return [itemName for itemName in dir(obj) if itemName[:len(findName)] == findName]

import os
print "dir"
print dir(os.path)
print ""
print "candi"
print candi(os.path, "split")


~(-_-)~ 짠

dir
['__all__', '__builtins__', '__doc__', '__file__', '__name__', '_getfullpathname
', 'abspath', 'altsep', 'basename', 'commonprefix', 'curdir', 'defpath', 'devnul
l', 'dirname', 'exists', 'expanduser', 'expandvars', 'extsep', 'getatime', 'getc
time', 'getmtime', 'getsize', 'isabs', 'isdir', 'isfile', 'islink', 'ismount', '
join', 'lexists', 'normcase', 'normpath', 'os', 'pardir', 'pathsep', 'realpath',
 'sep', 'split', 'splitdrive', 'splitext', 'splitunc', 'stat', 'supports_unicode
_filenames', 'sys', 'walk']

candi
['split', 'splitdrive', 'splitext', 'splitunc']


numpy 
파이썬에서 고속의 수학 연산을 지원합니다.
http://sourceforge.net/projects/numpy/


SciPy
numpy 를 기반으로 수학이나 과학 문제를 풀 때 쓸만한 유틸리티 모듈입니다. 다양한 인터폴레이션 함수 지원이 매력적입니다.
http://sourceforge.net/projects/scipy/files/



matplotlib
파이썬에서 그래프를 그려주는 모듈입니다.
http://matplotlib.sourceforge.net/
[출처 : http://www.imp17.com/tc/myevan/231]


서버 로그는 여러명의 사용자가 엄청난 양을 만들어내기 때문에
눈으로만 분석하기는 많이 어렵습니다. 

로그 분석기를 만들어서 체크하는 것이 좋은데;
문자열 처리가 빈약한 c/c++ 보다는 강력한 문자열 처리 기능을 제공하는 스크립트 언어를 사용하는 것이 좋습니다.

Jan 30 03:20:37 new connection from [192.168.1.1] to fd: 10
위와 같은 로그는 사람이 읽기에는 쉽지만, 컴퓨터에게 이해시키기란 조금 어렵습니다.

가장 무식한 방법은 글자수 센다음 파싱하는 것입니다.
하지만 이런 코드는 로그 형식이 조금만 바뀌어도 고치기가 어려워지죠;

다른 방법으로는 split 과 find 를 사용하는 방법이 있습니다.
아무리 간단하게 작성한다해도 하나의 형식에 5~10줄 정도의 코드가 나오게 됩니다.
문제는 이런 코드들은 나중에 재활용도 쉽지않은 정크 코드인지라; 매번 새로 작성해야합니다.
인터프리터 언어의 특성상 느리기까지 하죠.

로그를 분석하는 가장 좋은 방법은 정규 표현식이라 할 수 있습니다 -_-)/


import re

NEW_CONNECTION
= re.compile("new connection from \[(\d+.\d+.\d+.\d+)\] to fd: (\d+)")

if __name__ == "__main__":
    newConnection
= NEW_CONNECTION.search("new connection from [192.168.1.1] to fd: 10")
   
if newConnection:
       
print "ip:", newConnection.group(1)
       
print "fd:", newConnection.group(2)


파싱 코드는 re.compile("new connection from \[(\d+.\d+.\d+.\d+)\] to fd: (\d+)") 한줄로 끝나게 됩니다.
로그 형식이 바뀌더라도 정규 표현식 문구만 바꾸어주면 되죠 : )
[출처 : http://www.i-fam.net/water/93]


오랫만에 파이썬을 사용하다 보니 이것 저것 문제가 한둘이 아니다. 손에 익지 않은 도구를 잡고 사용한다는 것이 이렇게 힘든 일인가보다. 웹사이트에서 로딩한 문자열에서 원하는 문자열을 찾는 것이 잘 되지 않아 이리저리 고민하다보니 결국 문자열 인코딩이 다르다는 것을 알았다. 특히나 XML 은 UTF-8 인코딩을 주로 사용한다. 그러다보니 한글윈도우즈에 저장되는 한글코드와는 인코딩이 달라 동일한 글자라하더라도 Binary 코드 자체가 다른 것이다. 이를 일치하게 만들어 주기 위해서는 코드 변환을 해야한다.

코드를 변환하는 방법은 의외로 간단했다. 입력된 문자들은 모두 ASCII 코드로 인식되므로 일단 본래의 인코딩으로 인식할 수 있도록 만들어 줘야한다. 이때 사용하는 함수가 unicode(string, encoding) 함수이다. 이렇게 원래의 인코딩으로 변환을 하고 나면 다른 인코딩으로 변환이 가능하다. 한예를 들어보면 다음과 같다.

hanguel = unicode('한글', 'euc-kr').encode('utf-8')

이는 기본적으로 윈도우즈에서 사용되는 euc-kr 인코딩의 한글을 utf-8 인코딩의 한글로 변환하는 것이다. 하지만 파이썬 소스 파일 자체가 UTF-8 로 저장되어 있다면 말이 달라질 수도 있다. unicode('한글', 'utf-8') 만으로도 제대로 인코딩된 한글이 만들어 질 수 있는 것이다.

웹에서 utf-8 로 로딩한 문자열을 윈도즈에서 사용하는 문자열로 바꾸기 위해서는 반대의 과정을 수행하면 된다. 
소스가 다른 문자열의 비교나 검색에 문제가 있다면 일단 인코딩을 의심해 보자.

다음은 인코딩과 관련된 내용을 잘 정리한 외국 사이트의 내용을 번역한 것이다. 대충 번역한 것이라 참조만 하기 바라며, 본문 중 애매한 내용은 원문을 참조하기 바란다.

원본링크 : http://evanjones.ca/python-utf8.html (새 창으로 열기)

기초
파이썬에는 두가지 형태의 문자열이 있다.  바이트 문자열과 유니코드 문자열이 그것이다. 여러분이 생각하는 것처럼 바이트 문자열은 바이트의 시퀀스이다. 파이썬은 여러분 컴퓨터의 기본 로케일을 사용하여 바이트를 문자들로 변환한다. Mac OS X 에서는 기본 로케일이 실제로 UTF-8 이지만 그 외 대부분의 시스템에서는 기본 로케일이 아마도 ASCII 일 것이다.
다음 코드는 바이트 문자열을 생성한다.

byteString = "hello world! (in my default locale)"

그리고 다음 코드는 유니코드 문자열을 생성한다.

unicodeString = u"hello Unicode world!"

바이트 문자열을 유니코드 문자열로 변환하고 다시 바이트 문자열로 변환하는 코드는 다음과 같다.

s = "hello byte string"
u = unicode(s)
backToBytes = u.encode()

위의 코드는 여러분의 기본 문자집합을 변환에 사용한다. 그러나 로케일의 문자열집합을 신뢰하는 것은 나쁜 생각이다. 왜냐하면 여러분의 응용프로그램을 타일랜드에서 있는 누군가가 자신의 컴퓨터에서 실행하고자 한다면 바로 멈춰 버릴 것이기 때문이다. 대부분의 경우 문자열의 인코딩을 명확하게 정의해주는 것이 훨씬 낫다.

s = "hello normal string"
u = unicode(s, "utf-8")
backToBytes = u.encode("utf-8")

이제 바이트 문자열 s 는 유니코드 문자열 u 를 생성하여 UTF-8 바이트 시퀀스로 취급할 수 있을 것이다. 다음 라인은 UTF-8 의 표현인 u 를 다시 바이트 코드 문자열인 backToBytes 로 저장한다.

유니코드 문자열로 작업하기
감사하게도 파이썬은 모든 면에서 유니코드 문자열과 바이트 문자열이 동등하게 취급된다고 가정한다. 그러나 여러분은 만약 객체가 문자열인지 보려고 테스트 할 때 여러분 자신의 코드에 주의를 기울일 필요가 있다. 이런 식으로 하지마라.

if isinstance(s, str): # 나쁜 예: 유니코드 문자열에 대해 참이 아님

대신 basestring 이라는 일반 문자열 기반 클래스를 사용하라.

if isinstance(s, basestring): # 유니코드와 바이트 문자열에 대해 모두 참

UTF-8 파일 읽기
여러분은 파일에서 읽은 문자열을 수동을 변환할 수 있다. 그러나 다음과 같은 방법을 사용하면 좀더 쉽게 할 수 있다.

import codecs
fileObj = codecs.open( "someFile", "r", "utf-8")
u = fileObj.read() # 파일에 있는 UTF-8 바이트 들이 유니코드 문자열로 넘어옴

codecs module은 여러분이 하고자하는 모든 변환에 사용될 수 있다. 여러분은 또한 저장을 위해 파일을 열 수 있고, 여러분이 write 에 전달한 유니코드 문자열을 은 여러분이 선택한 인코딩이 무엇이건 변환되어 저장될 것이다.

XML과 minidom으로 작업하기
나는 XML 관련작업에 대부분 내가 친숙한 minidom module 을 사용한다. 불행히도 XML은 단지 바이트 문자열만을 다룬다. 따라서 minidom 함수에 넘겨주기 전에 유니코드 문자열로 인코딩해야 한다. 예제는 다음과 같다.

import xml.dom.minidom
xmlData = u"<français>Comment ça va ? Très bien ?</français>"
dom = xml.dom.minidom.parseString( xmlData )

마지막 열은 다음과 같은 예외를 발생시킨다.

UnicodeEncodeError: 'ascii' codec can't encode character '\ue7' in position 5: ordinal not in range(128). 

이런 에러 피하려면 minidom으로 문자열을 전달하기 전에 적절한 포맷으로 유니코드 문자열을 인코딩해야한다. 다음과 같이 해보자.

import xml.dom.minidom
xmlData = u"<français>Comment ça va ? Très bien ?</français>"
dom = xml.dom.minidom.parseString( xmlData.encode( "utf-8" ) )

minidom은 Latin-1 또는 UTF-16 등 어떠한 바이트 문자열의 어떤 포멧이던 다룰 수 있다. 하지만 XML 문서가 인코딩선언(eg. <?xml version="1.0" encoding="Latin-1"?>)을 가지고 있어야 정상적으로 작동한다. 만약 인코딩 선언이 빠진다면 minidom 은 인코딩을 UTF-8로 가정한다. 모든 시스템에서의 호환을 보장하려면, 당신의 모든 XML 문서에 인코딩선언을 포함하는 습관을 갖는 것이 좋다. 
minidom의 dom.toxml() 또는 dom.toprettyxml() 을 호출하여 생성되는 XML 결과는 유니코드 문자열로 넘어온다. encoding="utf-8" 이라는 여분의 매개변수를 전달하여 인코딩된 바이트 문자열을 얻을 수 있다.

+ Recent posts