콘텐츠로 건너뛰기
Home » Julia – 파일 다루기

Julia – 파일 다루기

Julia에서 파일을 여는 법

줄리아에서 파일을 읽고 쓰기 위해서는 IOStream 객체이를 이용한다. 스트림 객체를 만들기 위해서는 open() 함수를 사용한다. 이렇게 얻은 스트림객체는 read(), write(), seek()등 파일 입출력 관련 함수에서 사용된다. 가장 기본적인 문법으로 파일을 열어서 내용을 읽고 출력하는 코드를 작성해보면 다음과 같은데, 다른 많은 언어들과 큰 차이를 보이지 않는 것 같다.

fd = open("myfile.txt")
body = read(fd, String)
println(body)
close(fd)

C나 파이썬과 같은 언어와 다른 특이한 점은 파일 모드에서 r, rb의 구분이 따로 없다는 것이다. 스트림은 항상 이진 데이터를 가정한다. 그래서 read() 함수에는 읽어들일 데이터가 어떤 타입으로 만들어질 것인지, 그 타입을 정의한다.

open() 함수에서 사용되는 파일 모드는 다음과 같은 것들이 있다. 파일 모드는 인자로 전달하거나 키워드 인자로 특정 기능에 대해서 on/off 하는 방식으로 설정할 수 있다.

  • "r" : 읽기 모드
  • "w" : 쓰기 모드 (write=true)
  • "r+" : 읽고 쓰기 모드 (read=true, write=true)
  • "w+" : 읽고 쓰기 모드이면서 파일이 없으면 생성하고, 기존 파일을 지운다. (truncate=true, read=true)
  • "a" : 확장 모드 (append=true)
  • "a+" : 읽기 + 확장 모드 ( append=true, read=true)

이런 모드를 사용해서 파일을 열어 파일 스트림을 얻는 open() 의 원형은 다음과 같다.

open(filename::AbstractString, [mode::AbstractString]; lock=true) -> IOStream

이 외에도 open() 함수는 또다른 메소드를 가지고 있는데, 첫번째 인자로 함수를 받는 형태이다. 첫 인자는 IOStream 객체를 인자로 받는 함수가 되는데, open(f, filename) 함수는 파일을 열어서 함수 f에서 파일을 사용한다음, 파일을 닫는 동작까지를 한 번에 수행할 수 있다.

open(fd -> read(fd, String) |> println, "myfile.txt")

참고로 Julia에서는 이런 식으로 첫번째 인자에 콜백을 넘기는 형태의 함수가 많다. 이런 함수는 괄호 뒤에 do … end 블럭으로 첫번째 인자를 대체할 수 있다. 간단한 동작인 경우에는 위와 같은 익명 함수를 사용해도 좋은데, 복잡한 동작이라면 다음과 같은 방식으로 표현할 수 있다.

open("myfile.txt", "r") do fd
  read(fd, String) |> println
end

파일 읽기

파일의 내용을 읽는 것은 read() 함수와 그외 비슷한 몇 가지 함수를 사용할 수 있다. read() 함수는 위에서 살펴본대로 기본적으로 읽어야할 데이터의 타입을 지정하여 그 타입의 값을 하나 읽어서 리턴한다.

  • read(io, T) – 파일이나 버퍼, 스트림으로부터 T 타입 값을 읽어온다.
  • read(io, String) – 텍스트 파일/스트림 전체를 읽어 문자열로 리턴한다.
  • read(io, nbytes) – 파일에서 nbytes를 읽어서 바이트 배열 (UInt8 배열)을 리턴한다.

여기서 io 인자는 파일 디스크립터이거나 IOBuffer와 같은 스트림/버퍼도 될 수 있다. 파일의 내용을 읽기 위해서는 open() 에서 리턴된 스트림을 사용하는데, 그냥 파일 이름을 사용하면 해당 파일을 자동으로 열어서 읽어준다.

read()외에도 파일에서 값을 읽는 다른 함수들이 있다. /

  • readline(io, keep=false) : 파일에서부터 한 줄을 읽어들인다. keep=true이면 개행문자를 제거하지 않고 유지한다.
  • readlines(io, keep=false) : 파일의 내용 전체를 문자열로 모두 읽어 들이고 개행문자를 기준으로 나눠 문자열의 배열을 리턴한다.
  • readuntil(io, delim; keep=false) : 주어진 구분자(delimiter)를 만날 때 까지 파일에서 문자열을 읽는다.
  • readavailable(stream) : 스트림으로부터 읽을 수 있는 모든 바이트를 읽어들인다.
  • readchomp(x) : 파일로부터 문자열을 통째로 읽고, 끝에 달린 개행문자를 제거한다.

파일 탐색

파일에서 읽고 쓰는 위치를 변경한다. 스트림의 액세스 위치는 스트림 내부 속성으로 정해지기 때문에, 이 함수들은 파일에서의 위치를 리턴하지 않고 대부분 스트림 객체를 그대로 리턴한다.

  • seek(s, pos) : 스트림에서 특정 위치를 탐색한다. 파일의 맨 처음은 1이 아닌 0이다.
  • seekstart(s) / seekend(s) : 스트림의 처음과 끝으로 이동한다.
  • position(s) : 스트림에서 현재 위치를 리턴한다.
  • skip(s, offset) : seek()가 절대위치로 이동하는데 비해, skip()은 현재 위치에서 상대위치를 사용하여 이동한다.
  • skipchars(predicate, io) : predicate를 만족하는 문자를 건너뛴다. 예를 들어 현재 위치에서 공백문자들을 모두 건너 뛰겠다면 skipchars(isspace, buf) 와 같은 식으로 사용한다.
  • peek(s, T) : 파일의 현재 위치에서 T 타입의 값을 읽어본다. read()와 달리 읽기 위치가 다음 위치로 이동하지 않는다.
buf = IOBuffer(String(map(x->Char(x+96), 1:26)))
# 5번제 위치로 이동
seek(buf, 5)
position(buf)
# => 5
# 해당 위치의 문자 보기
peek(buf, Char)
# => 'f'
# 다시 처음 위치로
seekstart(buf)
positoin(buf)
# => 0
# 끝 위치로가서 다시 뒤로 1칸 돌아옴
seekend(buf)
skip(buf, -1)  # position(buf) == 25
peek(buf, Char)
# => 'z'

파일에 기록하기

파일에 내용을 기록하는 함수는 write(io, v)가 있다. 문자열 뿐만 아니라 어떤 줄리아 타입을 쓸 수 있으며, 실제 기록한 바이트수를 리턴한다.

buf = IOBuffer()
write(buf, "hello world\n")
write(buf, "welcome to julia\n")
String(take!(buf))
# "hello world\nwelcome to julia\n"

간단히 1000 이하의 소수를 텍스트 파일에 기록하는 코드를 살펴보자.

using Primes

# 소수를 기록하기
open("primes.txt", "w") do fd
  for i in filter(isprime, 1:1000)
    write(fd, string(i) * '\n')
  end
end

# 읽어서 출력하기
foreach(println, readlines("primes.txt"))

외부 명령

줄리아에서 외부 명령을 실행할 때에는 외부 명령 문법을 사용해서 command 객체를 만든다. 이를 run()등의 함수에 전달해서 외부 프로세스를 실행할 수 있는데, read*()류의 함수를 사용하면 해당 프로세스와 연결된 파이프를 통해서 해당 프로세스에서 표준 출력으로 내보내는 내용을 마치 파일 처럼 읽어들일 수 있다.

외부 명령은 백팃을 사용해서 둘러싼다. 이렇게하면 외부 명령을 실행할 Command 객체가 생성된다. Command 객체를 읽어들이려 할 때 해당 외부 명령이 실행되면서 데이터를 읽어들이게 된다.

results = readlines(`ls`)