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`)