트랙
/
Elixir
Elixir
/
연습 문제
/
파일 스니퍼
파일 스니퍼

파일 스니퍼

학습 연습 문제

소개

바이너리

앞서 비트스트링을 다루기 위해 제공되는 <<>> 특수 폼에서 봤듯이, Elixir는 바이너리 데이터를 다루는 우아한 문법을 제공해요.

바이너리 타입은 비트스트링 타입을 특수화한 것이에요. 비트스트링은 길이에 제한이 없지만(비트 개수가 얼마든 될 수 있어요), 바이너리는 비트 수가 8로 나누어떨어지는 경우를 말해요. 즉, 바이너리를 다룰 때는 흔히 바이트(8비트) 단위로 생각해요. 바이트 하나는 0부터 255까지의 정수를 나타낼 수 있어요. 바이트 값은 16진수인 0x00 - 0xFF로 다루는 경우가 많아요.

바이너리 리터럴은 비트스트링 특수 폼 <<>>로 정의해요. 바이너리 리터럴을 정의할 때는 정수 리터럴과 문자열 리터럴을 사용할 수 있어요. 255보다 큰 정수 값은 오버플로가 발생해서 정수의 마지막 8비트만 사용돼요. 기본적으로 값에는 ::binary 수식어가 적용돼요. <>/2 연산자로 바이너리를 이어 붙일 수 있어요.

<<255>> == <<0xFF>>
# Overflowing bits are truncated
<<256>> == <<0>>
<<2, 4, 6, 8, 10, 12, 14, 16>> == <<0x02, 0x04, 0x06, 0x08, 0x0A, 0x0C, 0x0E, 0x10>>

_널 바이트_는 <<0>>의 다른 이름이에요.

바이너리 데이터에 대한 패턴 매칭

패턴 매칭은 바이너리에도 적용돼요. 리스트에서 그러듯 바이너리 데이터의 일부분에 대해서도 패턴 매칭을 할 수 있어요.

# Ignore the first 8 bytes, match and bind the remaining to `body`
<<_::binary-size(8), body::binary>>

다른 패턴 매칭과 마찬가지로, 여러 함수 절 중에서 선택할 때 함수 시그니처에서도 이걸 사용할 수 있어요.

지침

사용자가 서버에 파일을 업로드해 다른 사용자와 공유할 수 있도록 하는 프로젝트를 진행하고 있어요. 업로드한 파일이 미디어 타입과 일치하는지 확인하는 함수를 작성하는 일을 맡았어요. 자료를 좀 찾아보니, 파일의 처음 몇 바이트는 대개 그 파일 형식만의 고유한 값이라서 일종의 서명 역할을 한다는 걸 알게 됐어요.

다음 표를 참고해요:

파일 형식 일반적인 확장자 미디어 타입 바이너리 '서명'
ELF "exe" "application/octet-stream" 0x7F, 0x45, 0x4C, 0x46
BMP "bmp" "image/bmp" 0x42, 0x4D
PNG "png" "image/png" 0x89, 0x50, 0x4E, 0x47, 0x0D, 0x0A, 0x1A, 0x0A
JPG "jpg" "image/jpg" 0xFF, 0xD8, 0xFF
GIF "gif" "image/gif" 0x47, 0x49, 0x46

1. 확장자가 주어졌을 때 예상 미디어 타입 반환하기

type_from_extension/1 함수를 구현해요. 이 함수는 파일 확장자(문자열)를 받아서 미디어 타입(문자열)을 반환하고, 확장자가 예상하는 값과 일치하지 않으면 nil을 반환해요.

FileSniffer.type_from_extension("exe")
# => "application/octet-stream"

FileSniffer.type_from_extension("txt")
# => nil

2. 바이너리 파일이 주어졌을 때 예상 미디어 타입 반환하기

type_from_binary/1 함수를 구현해요. 이 함수는 파일(바이너리)을 받아서 미디어 타입(문자열)을 반환하고, 파일 서명이 예상하는 값과 일치하지 않으면 nil을 반환해요.

file = File.read!("application.exe")
FileSniffer.type_from_binary(file)
# => "application/octet-stream"

file = File.read!("example.txt")
FileSniffer.type_from_binary(file)
# => nil

파일을 바이너리로 읽는 부분은 신경 쓰지 않아요. 그건 이미 처리되어 있고, 테스트가 인자로 넘겨준다고 가정해요.

3. 확장자와 바이너리 파일이 주어졌을 때 파일이 예상 형식과 일치하는지 확인하기

verify/2 함수를 구현해요. 이 함수는 파일(바이너리)과 확장자(문자열)를 받아서 :ok 또는 :error 튜플을 반환해요.

file = File.read!("application.exe")

FileSniffer.verify(file, "exe")
# => {:ok, "application/octet-stream"}

FileSniffer.verify(file, "png")
# => {:error, "Warning, file format and file extension do not match."}
GitHub에서 편집 링크가 새 창이나 탭에서 열려요
Elixir Exercism

파일 스니퍼 문제를 시작해 볼 준비가 됐나요?

Exercism에 가입하고 Elixir 트랙을 개념 58개연습 문제 168개, 그리고 실제 사람의 멘토링과 함께 배우고 익혀 보세요. 모두 무료예요.