파일과 텍스트를 처리하기

우리는 무엇을 다룰 것인가?
  • 파일을 여는 법
  • 연 파일에 대하여 읽고 쓰는 법
  • 파일을 닫는 법
  • 단어 계수기 만들기
  • 파일을 다루는 것은 때로 초보자에게 문제가 된다. 그렇지만 왜 그런지는 나에게 이해가 안간다. 프로그래밍의 관점에서 파일은 여러분이 워드프로세서 혹은 다른 어플리케이션에서 사용하는 파일과 다르지 않다: 여러분은 그것을 연다 , 뭔가 일을 하고 그리고 나서 그것을 다시 닫는다

    가장 큰 차이점은 프로그램에서 여러분은 파일을 연속적으로 sequentially 접근한다는 것인데, 다시 말하면, 여러분은 처음부터 시작하여 한번에 한 라인씩을 읽는다는 것이다. 실제로 워드 프로세서는 때때로 같은 일을 한다, 그것은 단지 전체 파일을 메모리에 보관하며 여러분은 그것에 대해 작업을 하고 그리고는 그것을 모두 다시 쓰고 여러분은 그것을 닫는다. 다른 차이점은 여러분이 보통 그 파일을 읽기전용 혹은 쓰기전용으로만 파일을 연다는 것이다. 여러분은 처음부터 새로운 파일을 생성하거나 (혹은 존재하는 파일을 덮어써서) 혹은 존재하는 파일의 뒤에 추가 appending하여 작성할 수 있다

    파일을 처리하는 중에 여러분이 할 수있는 또 다른 하나는 여러분이 처음으로 되돌아 갈 수 있다는 것이다.

    파일 - 입력과 출력

    실 예로 살펴보자. 우리는 menu.txt라고 부르는 파일이 존재하며 음식의 목록을 보유하고 있다고 가정할 것이다:

    spam & eggs
    spam & chips
    spam & spam
    

    이제 우리는 그 파일을 일고 출력하는 프로그램을 작성할 것이다. - 유닉스의 'cat' 명령어나 도스의 'typy'명령어 같은.

    # First open the file to read(r)
    inp = open("menu.txt","r")
    # read the file into a list then print
    # each item
    for line in inp.readlines():
        print line
    # Now close it again
    inp.close()
    

    주의 1: open()함수는 두개의 인수를 취한다. 첫째는 파일이름이고 (우리가 여기에서 한 것같이 문자열 혹은 변수로 넘겨질 수 있다) 두번째는 모드mode이다. 모드는 우리가 연 그 파일이 읽기(r) 혹은 쓰기writing(w)인지를 결정한다. 그리고 또한 아스키형 텍스트인가 혹은 이진문서인가를 결정한다. - a와 b를 'r' 또는 'w' 추가함으로써, 다음예와 같이 : open(fn,"rb")

    주의 2: 우리는 함수를 파일 변수뒤에 놓고 사용하여 그 파일을 읽고 쓴다. 이러한 표기법은 메쏘드 요청 method invocation이라고 알려져 있으며 우리는 처음으로 살짝 객체 지향 Object Orientation을 엿보게 되었다. 어떤 식으로든 그것이 모듈과 관계있는 것이라고 깨닫기만 한다면, 지금은 그것에 대해 걱정하지 마라. 여러분은 파일 변수를, 파일에 관한 처리를 하고 우리가 파일 형태의 변수를 작성할 때마다 자동적으로 수입하는 함수를 담고 있는 모듈에 대한 참조라고 간주할 수 있다.

    여러분이 어떻게 기다란 파일에 대처할 수 있는지 생각해 보라, 무엇보다도 여러분은 한번에 한 라인씩 그 파일을 읽을 필요가 있을 것이다.(파이썬에서 readlines()대신에 readline() 를 사용함으로써) 여러분은 그러면 각 라인에 따라 증가하는 line_count 변수를 사용하게 되어서 그리고 ( 25라인짜리 스크린에서) 그 변수가 25 인지 아닌지 알아보아야 할지도 모른다. 그렇다면, 여러분은 사용자에게 line_count 변수를 0으로 재설정하고 계속하기 전에 키(엔터키)를 누르도록 요청한다. 여러분은 연습으로 그것을 시도해보고 싶을 것이다.

    실제로 그것이 모든것이다. 여러분은 파일을 열고, 읽어들이고 여러분이 원하는데로 처리한다. 끝나고 나면 여러분은 파일을 닫는다. 파이썬에서 'copy' 명령어를 작성하기 위해서는, 우리는 간단히 새로운 파일을 쓰기 모드로 열어서 라인들을 그것들을 화면에 출력하는 대신에 그 파일에 쓰기만 하면 된다. 다음과 같이:

    # Create the equivalent of: COPY MENU.TXT MENU.BAK
    
    # First open the files to read(r) and write(w)
    inp = open("menu.txt","r")
    outp = open("menu.bak","w")
    
    # read the file into a list then copy to
    # new file
    for line in inp.readlines():
        outp.write(line)
    
    print "1 file copied..."
    
    # Now close the files
    inp.close()
    outp.close()
    

    내가 단지 print 서술문을 추가하여 사용자에게 어떤일이 실제로 일어났는지 재확인시키고 있다는 것을 눈치 챘는가? 이러한 종류의 사용자 피드백user feedback은 보통은 좋은 생각이다.

    마지막 문제는 여러분은 존재하는 파일의 끝에다 추가하기를 원한다는 것이다. 그러기 위한 한가지 방법은 파일을 입력으로 열고, 그 안의 데이타를 리스트로 읽어 들이고, 데이타를 그 리스트에 추가한다 그리고 전체 리스트를 출력하여 예전 파일을 새로운 버전으로 작성한다. 만약 그 파일이 짧다면 그것을 별 문제가 되지 않으나 만약 그 파일이 대단히 길다면, 100메가 이상이나 된다면, 그러면 여러분은 간단하게 메모리를 다 써버려서 그 리스트를 유지할 수 없을 것이다. 다행스럽게도 우리는 open()함수에 접근할 수 있는 "a" 라는 또 다른 모드가 있는데 그것은 우리가 존재하는 파일에 단지 쓰기만으로도 직접 추가할 수 있도록 해준다. 더 좋은 것은, 만약 파일이 존재하지 않는다면 여러분이 "W"를 지정한 것같이 새로운 파일이 열린다는 것이다.

    예를 들어, 우리가 에러 메시지를 획득하기 위하여 우리가 사용한 로그 파일을 가지고 있다고 가정해보자 우리는 존재하는 메시지를 삭제하기를 원하지 않는다 그래서 우리는 그 에러를 추가하기로 결정한다, 다음과 같이:

    def logError(msg):
       err = open("Errors.log","a")
       err.write(msg)
       err.close()
    

    실제 세계에서 우리는 아마도 파일의 크기를 어떤 면으로는 제한하기를 원할지도 모른다. 흔한 테크닉은 날짜에 기초한 파일 이름을 생성하는 것이다, 그렇게 하여 날짜가 변하면 우리는 자동적으로 새로운 파일을 만들게 되고 시스템 관리자가 특정한 날의 에러를 찾기 쉬우며 필요치 않다면 오래된 에러 파일을 치워 버리기가 용이하다.

    단어 세기

    이제 이전 장에서 언급한 단어 계수 프로그램을 다시 방문해 보자. 다음과 같은 의사 코드 ( Pseudo Code)를 상기하라:

    def numwords(s):
        list = split(s) # list with each element a word
        return len(list) # return number of elements in list
    
    for line in file:
        total = total + numwords(line) # accumulate totals for each line
    print "File had %d words" % total
    

    이제 우리는 파일로부터 라인들을 가져오는 방법을 알고있다. numwords()의 몸체를 살펴보자 먼저 우리는 한라인에 있는 단어들의 목록을 작성하기를 원한다. string모듈에 대한 파이썬의 참조 문서를 살펴봄으로써 우리는 거기에 split라고 부르는 함수가 있는 것을 알게된다. 그것은 문자열을 공백으로 (또는 우리가 정의한 다른 문자로) 격리된 필드의 리스트로 분리한다. 마지막으로, 다시 파이썬 문서를 참조해보면 우리는 내장된 len() 함수가 리스트안에 있는 요소들의 개수를 반환한다는 것을 알게 되는데, 그것은 우리의 경우에 문자열에 있는 단어의 개수가 된다 - 정확히 우리가 원하는데로

    그래서 최종 코드는 다음과 같이 보인다:

    import string
    def numwords(s):
        list = string.split(s) # need to qualify split() with string module
        return len(list) # return number of elements in list
    
    inp = open("menu.txt","r")
    total = 0  # initialise to zero; also creates variable
    
    for line in inp.readlines():
        total = total + numwords(line) # accumulate totals for each line
    print "File had %d words" % total
    
    inp.close()
    

    '&' 문자를 단어로 세기 때문에 (여러분이 그것을 단어라고 생각할지라도) 물론 이것은 완전히 옳지는 않다. 또한, 이것은 하나의 파일(menu.txt)에만 사용되어 진다. 그러나 명령어 라인( argv[1])으로 부터 혹은 raw_input()을 통하여 파일이름을 읽기 위하여 우리가 '사용자와 대화하기' 섹션에서 본 것처럼 그것을 변경하는 것은 그렇게 어렵지 않다. 나는 독자들을 위하여 그것을 연습문제로 남겨둔다.

    BASIC 그리고 Tcl

    베이직과 티클은 자신만의 파일 처리 메카니즘을 제공한다. 그것들은 파이썬과 그렇게 다르지 않아서 나는 단순히 여러분에게 'cat' 프로그램을 두개의 버젼으로 보여 주고 끝내겠다.

    BASIC 버전

    베이직은 흐름 streams 이라고 불리우는 개념을 사용하여 파일을 구별한다. 이러한 흐름은 숫자로 세어지기 때문에 베이직 파일 처리가 지루할 수도 있다. ??? 라고 부르는 간단한 함수를 사용하여 이것을 피할 수 있다. 그 함수는 자유로운 다음의 흐름 숫자를 반환한다. 여러분이 이것을 변수에 저장하면 여러분은 흐름/파일이 어느 숫자를 가지는지에 관하여 혼란스러울 필요가 전혀 없다.

    INFILE = FREEFILE
    OPEN "TEST.DAT" FOR INPUT AS INFILE
    REM Check for EndOfFile(EOF) then
    REM read line from input and print it
    DO WHILE NOT EOF(INFILE)
        LINE INPUT #INFILE, theLine
        PRINT theLine
    CLOSE #INFILE
    

    Tcl 버전

    지금까지 그 패턴은 명료하다. 여기에 티클 버전이 있다:

    set infile [open "Test.dat" r]
    while { [gets $infile line] >= 0} {
         puts $line
         }
    close $infile
    
    기억해야할 것
    • 파일을 사용하기 전에 열어라
    • 파일은 보통 읽거나 쓸수 있지만 동시에는 되지 않는다
    • 파이썬의 readlines() 함수는 파일에 있는 모든 라인을 읽는다. 반면에 readline() 함수는 한번에 한 라인만을 읽는다. 이것으로 메모리를 절약하는데 도움이 된다.
    • 사용하고 난 다음에는 닫아라.
    Previous  Next  Contents


    이 페이지에 대하여 질문 혹은 제안사항이 있으면 다음주소로 나에게 전자메일을 보내라: agauld@crosswinds.net