더 많은 포트 생성자·절차·이벤트
더 많은 포트 생성자·절차·이벤트 (More Port Constructors, Procedures, and Events)
racket/port 라이브러리는 포트를 문자열·리스트·바이트 문자열로 변환하고, 여러 포트를 조합하고, 위치 정보를 조정하고, 동기화 가능한 이벤트로 읽는 등 실용적인 포트 헬퍼들을 모아둔 절이에요. 이 절에서 문서화한 바인딩들은 racket/port와 racket 라이브러리가 제공하지만 racket/base는 제공하지 않으니, (require racket/port)로 불러와야 해요.
출처: Racket Reference
본문
포트의 문자열·리스트 변환 (Port String and List Conversions)
포트에 들어 있는 데이터를 문자열·바이트 문자열·리스트 형태로 한 번에 뽑아내는 함수들이에요.
(port->list [r in]) → (listof any/c)
r : (input-port? . -> . any/c) = readin : input-port? = (current-input-port)
in에서 r을 계속 호출해 eof가 나올 때까지 모은 값을 리스트로 돌려줘요.
예시:
> (define (read-number input-port)
(define char (read-char input-port))
(if (eof-object? char)
char
(string->number (string char))))
> (port->list read-number (open-input-string "12345"))
'(1 2 3 4 5)
(port->string [in #:close? close?]) → string?
in : input-port? = (current-input-port)close? : any/c = #f
in에서 모든 문자를 읽어 하나의 문자열로 돌려줘요. close?가 #f가 아니면 입력 포트를 닫아요.
예시:
> (port->string (open-input-string "hello world"))
"hello world"
버전 6.8.0.2에서 #:close? 인자가 추가됐어요.
(port->bytes [in #:close? close?]) → bytes?
in : input-port? = (current-input-port)close? : any/c = #f
port->string과 같지만 모든 바이트를 읽어 바이트 문자열로 돌려줘요. close?가 #f가 아니면 입력 포트를 닫아요.
예시:
> (port->bytes (open-input-string "hello world"))
#"hello world"
버전 6.8.0.2에서 #:close? 인자가 추가됐어요.
(port->lines [in #:line-mode line-mode #:close? close?]) → (listof string?)
in : input-port? = (current-input-port)line-mode : (or/c 'linefeed 'return 'return-linefeed 'any 'any-one) = 'anyclose? : any/c = #f
in에서 모든 문자를 읽어 줄 단위로 나눠 리스트로 돌려줘요. line-mode 인자는 read-line의 두 번째 인자와 같지만, 기본값은 'linefeed 대신 'any예요. close?가 #f가 아니면 입력 포트를 닫아요.
예시:
> (port->lines
(open-input-string "line 1\nline 2\n line 3\nline 4"))
'("line 1" "line 2" " line 3" "line 4")
버전 6.8.0.2에서 #:close? 인자가 추가됐어요.
(port->bytes-lines [in #:line-mode line-mode #:close? close?]) → (listof bytes?)
in : input-port? = (current-input-port)line-mode : (or/c 'linefeed 'return 'return-linefeed 'any 'any-one) = 'anyclose? : any/c = #f
port->lines와 같지만 바이트를 읽어 read-bytes-line처럼 줄 단위로 모아요. close?가 #f가 아니면 입력 포트를 닫아요.
예시:
> (port->bytes-lines
(open-input-string "line 1\nline 2\n line 3\nline 4"))
'(#"line 1" #"line 2" #" line 3" #"line 4")
버전 6.8.0.2에서 #:close? 인자가 추가됐어요.
(display-lines lst [out #:separator separator]) → void?
lst : list?out : output-port? = (current-output-port)separator : any/c = #"\n"
lst의 각 요소를 display로 out에 출력하고 각 요소 뒤에 separator를 붙여요.
(call-with-output-string proc) → string?
proc : (output-port? . -> . any)
모든 출력을 문자열로 모아주는 출력 포트를 proc에 넘겨 호출하고, 그 문자열을 돌려줘요.
proc에 넘겨지는 포트는 open-output-string이 만든 것과 같지만 dup-output-port로 감싸져 있어서, proc이 get-output-string으로 포트의 내용을 꺼낼 수는 없어요. 제어가 다시 proc 안으로 되돌아오면 포트는 새 데이터를 계속 모으고, call-with-output-string은 이전 데이터와 새로 모은 데이터를 모두 돌려줘요.
(call-with-output-bytes proc) → bytes?
proc : (output-port? . -> . any)
call-with-output-string과 같지만 누적된 결과를 문자열 대신 바이트 문자열로 돌려줘요. 게다가 call-with-output-bytes가 반환할 때 포트의 내용을 비우므로, 제어가 다시 proc으로 되돌아와 두 번째로 반환하면 새로 누적된 바이트만 돌려줘요.
(with-output-to-string proc) → string?
proc : (-> any)
다음 식과 같아요.
(call-with-output-string
(lambda (p) (parameterize ([current-output-port p])
(proc))))
(with-output-to-bytes proc) → bytes?
proc : (-> any)
다음 식과 같아요.
(call-with-output-bytes
(lambda (p) (parameterize ([current-output-port p])
(proc))))
(call-with-input-string str proc) → any
str : string?proc : (input-port? . -> . any)
(proc (open-input-string str))과 같아요.
(call-with-input-bytes bstr proc) → any
bstr : bytes?proc : (input-port? . -> . any)
(proc (open-input-bytes bstr))과 같아요.
(with-input-from-string str proc) → any
str : string?proc : (-> any)
다음 식과 같아요.
(parameterize ([current-input-port (open-input-string str)])
(proc))
(with-input-from-bytes bstr proc) → any
bstr : bytes?proc : (-> any)
다음 식과 같아요.
(parameterize ([current-input-port (open-input-bytes str)])
(proc))
포트 만들기 (Creating Ports)
여러 포트를 하나로 이어 붙이거나, 기존 포트를 감싸 새 포트를 만드는 함수들이에요.
(input-port-append close-at-eof? in ... [#:name name]) → input-port?
close-at-eof? : any/cin : input-port?name : any/c = (map object-name in)
임의 개수의 입력 포트를 받아 하나의 입력 포트를 돌려줘요. 그 입력 포트에서 읽으면 주어진 입력 포트들에서 순서대로 바이트(그리고 특수한 비바이트 값)를 끌어와요. close-at-eof?가 참이면, 각 포트에서 eof를 만나면 그 포트를 닫거나, 결과 입력 포트가 닫힐 때 각 포트를 닫아요. 그렇지 않으면 반환된 입력 포트에서 읽지 않은 데이터는 원래 입력 포트에 남아 계속 읽을 수 있어요.
name 인자는 반환된 입력 포트의 object-name이 보고하는 이름을 결정해요.
데이터가 생길 때마다 여러 입력 포트의 데이터를 섞는 merge-input도 참고하세요. 버전 6.90.0.19에서 name 인자가 추가됐어요.
(make-input-port/read-to-peek name read-in fast-peek close [get-location count-lines! init-position buffer-mode buffering? on-consumed]) → input-port?
name : any/cread-in : (bytes? . -> . (or/c exact-nonnegative-integer? eof-object? procedure? evt?))fast-peek : (or/c #f (bytes? exact-nonnegative-integer? (bytes? exact-nonnegative-integer? . -> . (or/c exact-nonnegative-integer? eof-object? procedure? evt? #f)) . -> . (or/c exact-nonnegative-integer? eof-object? procedure? evt? #f)))close : (-> any)get-location : (or/c (-> (values (or/c exact-positive-integer? #f) (or/c exact-nonnegative-integer? #f) (or/c exact-positive-integer? #f))) #f) = #fcount-lines! : (-> any) = voidinit-position : exact-positive-integer? = 1buffer-mode : (or/c (case-> ((or/c 'block 'none) . -> . any) (-> (or/c 'block 'none #f))) #f) = #fbuffering? : any/c = #fon-consumed : (or/c ((or/c exact-nonnegative-integer? eof-object? procedure? evt?) . -> . any) #f) = #f
make-input-port와 비슷하지만, 주어진 read-in이 이벤트를 반환하면 그 이벤트의 값이 0이어야 해요. 결과 포트의 peek 연산은 특수한 비바이트 값을 처리할 수 있는 방식으로 read-in을 기반으로 자동 구현돼요. progress-event와 commit 연산도 자동으로 구현돼요. 결과 포트는 스레드에 안전하지만 kill에 안전하진 않아요(즉, 포트를 쓰는 동안 스레드가 종료되거나 일시 중단되면 포트가 손상될 수 있어요).
read-in, close, get-location, count-lines!, init-position, buffer-mode 절차들은 make-input-port와 같아요.
fast-peek 인자는 #f이거나 세 인자를 받는 절차예요: peek을 받을 바이트 문자열, skip 개수, 두 인자를 받는 절차. fast-peek 절차는 요청된 peek을 직접 구현하거나, 세 번째 인자에 위임해 peek을 구현할 수 있어요. fast-peek은 peek 요청에 progress 이벤트가 연관된 경우에는 사용되지 않아요.
buffering? 인자는 새 포트의 사용자가 당장 요구하는 것보다 더 많은 문자를 읽도록 read-in을 호출할 수 있는지 결정해요. buffer-mode가 #f가 아니면 buffering?이 초기 버퍼 모드를 결정하고, 버퍼링 변경 후에는 새 모드가 'block일 때만 buffering?이 활성화돼요.
on-consumed가 #f가 아니면, 데이터를 단순히 peek이 아닌 포트에서 읽거나(commit) 할 때 호출돼요. on-consumed의 인자는 포트의 읽기 절차의 결과 값이므로, 정수이거나 read-in의 어떤 결과일 수 있어요.
(make-limited-input-port in limit [close-orig?]) → input-port?
in : input-port?limit : exact-nonnegative-integer?close-orig? : any/c = #t
in에서 내용을 끌어오되, limit 바이트(그리고 비바이트 특수 값)를 읽은 후 eof를 보고하는 포트를 돌려줘요. close-orig?가 참이면 반환된 포트가 닫힐 때 원래 포트도 닫혀요.
바이트는 반환된 포트에서 소비될 때만 in에서 소비돼요. 특히 반환된 포트를 peek하면 원래 포트를 peek해요.
결과 포트를 쓰는 동안 in을 직접 쓰면, 포트가 제공하는 제한 바이트가 원래 포트 스트림의 연속된 부분일 필요는 없어요.
(make-pipe-with-specials [limit in-name out-name]) → input-port? output-port?
limit : exact-nonnegative-integer? = #fin-name : any/c = 'pipeout-name : any/c = 'pipe
입력 포트와 출력 포트 두 개를 돌려줘요. make-pipe가 반환한 포트처럼 동작하지만, write-special 같은 절차로 쓴 비바이트 값과 get-byte-or-special 같은 절차로 읽은 값을 지원해요.
limit 인자는 파이프의 최대 용량(바이트)을 결정하지만, limit에 도달하기 전에 특수 값이 파이프에 쓰이면 이 제한은 비활성화돼요. 특수 값이 파이프에서 읽힌 후 제한이 다시 활성화돼요.
선택적인 in-name과 out-name 인자는 결과 포트들의 이름을 결정해요.
(combine-output a-out b-out) → output-port?
a-out : output-port?b-out : output-port?
두 출력 포트를 받아 원래 포트들을 결합한 새 출력 포트 하나를 돌려줘요. 결합된 포트에 쓰면, 먼저 가능한 한 많은 바이트를 a-out에 쓰고, 그다음 같은 수의 바이트를 b-out에 쓰려고 해요. 그것이 성공하지 못하면 남은 부분이 버퍼링되고 포트들이 평준화될 때까지 더 이상의 쓰기는 통과할 수 없어요. 각 포트가 준비됐다고 보고할 때 포트는 (동기화 목적상) 준비된 상태예요. 다만 첫 포트는 두 번째 포트가 동기화되기를 기다리는 동안 준비 상태가 풀릴 수 있으므로, 두 포트가 동시에 준비된다고 보장할 수는 없어요. 결합된 포트를 닫는 것은 b-out에 남은 모든 바이트를 쓴 뒤에 이뤄져요.
버전 7.7.0.10에 추가됐어요.
(merge-input a-in b-in [buffer-limit]) → input-port?
a-in : input-port?b-in : input-port?buffer-limit : (or/c exact-nonnegative-integer? #f) = 4096
두 입력 포트를 받아 새 입력 포트 하나를 돌려줘요. 새 포트는 두 원래 포트의 데이터를 병합하므로, 두 원래 포트 중 어느 쪽이라도 데이터를 쓸 수 있으면 새 포트에서 읽을 수 있어요. 원래 포트들의 데이터는 번갈아 섞여요. 원래 포트에서 eof를 읽으면 그 포트는 더 이상 새 포트에 문자를 기여하지 않아요. 두 원래 포트 모두에서 eof를 읽은 후에는 새 포트가 eof를 돌려줘요. 병합된 포트를 닫아도 원래 포트는 닫히지 않아요.
선택적인 buffer-limit 인자는 a-in과 b-in에서 버퍼링할 바이트 수를 제한해서, 병합 과정이 병합 데이터의 소비 속도보다 임의로 앞서가지 않도록 해요. #f 값은 제한을 비활성화해요. make-pipe-with-specials처럼, 제한에 도달하기 전에 입력 포트 중 하나가 특수 값을 만들면 buffer-limit은 적용되지 않아요.
섞는 대신 입력 스트림을 이어 붙이는 input-port-append도 참고하세요.
(open-output-nowhere [name special-ok?]) → output-port?
name : any/c = 'nowherespecial-ok? : any/c = #t
(차단 없이) 받은 모든 출력을 버리는 출력 포트를 만들어 돌려줘요. name 인자는 포트의 이름으로 쓰여요. special-ok? 인자가 참이면 결과 포트는 write-special을 지원하고, 그렇지 않으면 지원하지 않아요.
(open-input-nowhere [name]) → input-port?
name : any/c = 'nowhere
(차단 없이) 항상 eof를 돌려주는 입력 포트를 만들어 돌려줘요. name 인자는 포트의 이름으로 쓰여요. 버전 8.15.0.2에 추가됐어요.
(peeking-input-port in [name skip #:init-position init-position]) → input-port?
in : input-port?name : any/c = (object-name in)skip : exact-nonnegative-integer? = 0init-position : exact-positive-integer? = 1
in을 peek하여 내용이 결정되는 입력 포트를 돌려줘요. 즉 결과 포트는 내부 skip 개수를 가지며, 포트를 읽을 때마다 내부 skip 개수로 in을 peek하고, 성공적으로 peek한 데이터 양에 따라 skip 개수를 증가시켜요.
선택적인 name 인자는 결과 포트의 이름이에요. skip 인자는 포트의 초기 skip 개수로 기본값은 0이에요.
결과 포트의 초기 위치(file-position이 보고하는)는 in의 위치와 무관하게 (- init-position 1)이에요.
결과 포트는 버퍼링을 지원하고, 'block 버퍼 모드는 포트가 요청된 것보다 더 멀리 in을 peek할 수 있게 해요. 결과 포트의 초기 버퍼 모드는 'block인데, in이 버퍼 모드를 지원하고 그 모드가 처음에 'none일 때는 예외예요(즉, in이 버퍼링을 지원하면 초기 버퍼 모드는 in에서 가져와요). in이 버퍼링을 지원하면, file-stream-buffer-mode로 결과 포트의 버퍼 모드를 조정하면 in의 버퍼 모드도 조정돼요.
예를 들어, peeking 포트에서 읽으면 원래 포트에서 읽을 때와 같은 답을 볼 수 있어요:
예시:
> (define an-original-port (open-input-string "123456789"))
> (define a-peeking-port (peeking-input-port an-original-port))
> (file-stream-buffer-mode a-peeking-port 'none)
> (read-string 3 a-peeking-port)
"123"
> (read-string 3 an-original-port)
"123"
원래 포트에서의 읽기는 별도 내부 카운터를 유지하는 peeking 포트에는 보이지 않으므로, 두 포트에서 번갈아 읽으면 혼란스러운 결과를 낼 수 있음을 주의하세요. 앞선 예를 이어서, peeking 포트에서 세 문자를 더 읽으면 포트의 456을 건너뛰게 돼요(위에서 버퍼링을 껐기 때문):
예시:
> (read-string 3 a-peeking-port)
"789"
만약 a-peeking-port의 버퍼 모드를 그대로 두었다면, 마지막 read-string은 앞서 an-original-port에서 바이트를 버퍼링한 결과로 아마 "456"을 만들었을 거예요.
버전 6.1.0.3에서 변경: file-stream-buffer-mode로 버퍼링과 버퍼 모드 조정을 활성화하고, 포트의 초기 버퍼 모드를 in의 것으로 설정.
(reencode-input-port in encoding [error-bytes close? name convert-newlines? enc-error]) → input-port?
in : input-port?encoding : string?error-bytes : (or/c #f bytes?) = #fclose? : any/c = #fname : any/c = (object-name in)convert-newlines? : any/c = #fenc-error : (string? input-port? . -> . any) = (lambda (msg port) (error ...))
in에서 바이트를 끌어오지만, (bytes-open-converter encoding-str "UTF-8")로 바이트 스트림을 변환하는 입력 포트를 만들어요. 게다가 convert-newlines?가 참이면, "\r\n", "\r\u0085", "\r", "\u0085", "\u2028"의 UTF-8 인코딩에 해당하는 디코딩 시퀀스들이 모두 "\n"의 UTF-8 인코딩으로 변환돼요.
error-bytes가 제공되고 #f가 아니면, 변환 오류를 일으키는 in의 바이트 대신 주어진 바이트 시퀀스가 사용돼요. 그렇지 않으면 변환을 만나면 enc-error가 호출되는데, 이는 예외를 일으켜야 해요.
close?가 참이면 결과 입력 포트를 닫을 때 in도 닫아요. name 인자는 결과 입력 포트의 이름으로 쓰여요.
비버퍼 모드에서 결과 입력 포트는 요청을 충족하는 데 필요한 만큼만 in에서 바이트를 끌어오려고 해요. 이를 위해 입력 포트는 n바이트 요청을 충족하려면 최소 n바이트를 읽어야 한다고 가정해요. (포트가 이미 일부 바이트를 끌어왔더라도 그 바이트들이 불완전한 인코딩 시퀀스를 이루는 한 마찬가지예요.)
(reencode-output-port out encoding [error-bytes close? name newline-bytes enc-error]) → output-port?
out : output-port?encoding : string?error-bytes : (or/c #f bytes?) = #fclose? : any/c = #fname : any/c = (object-name out)newline-bytes : (or/c #f bytes?) = #fenc-error : (string? output-port? . -> . any) = (lambda (msg port) (error ...))
바이트를 out에 전달하되, (bytes-open-converter "UTF-8" encoding-str)로 바이트 스트림을 변환하는 출력 포트를 만들어요. 게다가 newline-bytes가 #f가 아니면, 포트에 쓰인 "\n"의 UTF-8 인코딩인 바이트는 먼저 newline-bytes로 변환돼요(UTF-8에서 encoding-str로의 변환을 적용하기 전에).
error-bytes가 제공되고 #f가 아니면, 출력 포트로 보내져 변환 오류를 일으키는 바이트 대신 주어진 바이트 시퀀스가 사용돼요. 그렇지 않으면 enc-error가 호출되는데, 이는 예외를 일으켜야 해요.
close?가 참이면 결과 출력 포트를 닫을 때 out도 닫아요. name 인자는 결과 출력 포트의 이름으로 쓰여요.
결과 포트는 버퍼링을 지원하고, 초기 버퍼 모드는 (or (file-stream-buffer-mode out) 'block)이에요. 'block 모드에서는 포트의 버퍼가 가득 차거나 명시적으로 flush를 요청할 때만 비워져요. 'line 모드에서는 줄바꿈이나 캐리지 리턴 바이트가 포트에 쓰일 때마다 버퍼가 비워져요. 'none 모드에서는 매 쓰기 후에 포트의 버퍼가 비워져요. 'line 또는 'none의 암묵적 flush는 불완전한 인코딩 시퀀스의 일부인 바이트를 버퍼에 남겨둬요.
결과 출력 포트는 원자적 쓰기를 지원하지 않아요. 가장 최근에 쓰인 바이트가 불완전한 인코딩 시퀀스를 이루면, 출력 포트에 대한 명시적 flush나 special-write가 멈출 수 있어요.
포트가 버퍼링되면 현재 plumber에 flush 콜백이 등록되어 버퍼를 비워요.
(dup-input-port in [close?]) → input-port?
in : input-port?close? : any/c = #f
in에서 직접 끌어오는 입력 포트를 돌려줘요. 결과 포트를 닫는 것은 close?가 #t일 때만 in을 닫아요.
새 포트는 in의 포트 읽기 핸들러로 초기화되지만, 결과 포트에 핸들러를 설정해도 in에서 직접 읽는 것에는 영향이 없어요.
(dup-output-port out [close?]) → output-port?
out : output-port?close? : any/c = #f
데이터를 out에 직접 전달하는 출력 포트를 돌려줘요. 결과 포트를 닫는 것은 close?가 #t일 때만 out을 닫아요.
새 포트는 out의 포트 표시 핸들러와 포트 쓰기 핸들러로 초기화되지만, 결과 포트에 핸들러를 설정해도 out에 직접 쓰는 것에는 영향이 없어요.
(relocate-input-port in line column position [close?] #:name name) → input-port?
in : input-port?line : (or/c exact-positive-integer? #f)column : (or/c exact-nonnegative-integer? #f)position : exact-positive-integer?close? : any/c = #tname : (object-name in)
위치 정보(그리고 아마 이름)를 보고하는 방식만 제외하고 in과 동등한 입력 포트를 만들어요. 결과 포트의 내용은 in의 남은 내용으로 시작하고, 주어진 line, column, position에서 시작해요. line이나 column이 #f이면 그 줄과 열은 항상 #f로 보고된다는 뜻이에요.
line과 column 값은 in과 결과 포트 모두에서 줄 세기(line counting)가 활성화된 경우에만 사용돼요(보통 port-count-lines!로). column 값은 첫 줄(즉, 번호가 매겨진 줄)의 열을 결정하고, 이후 줄은 열 0에서 시작해요. 주어진 position은 줄 세기가 활성화되지 않아도 사용돼요.
결과 포트에서 줄 세기가 켜져 있으면, 결과 포트 대신 in에서 읽으면 결과 포트의 위치 보고를 증가시켜요. 그렇지 않으면 in에서 데이터를 읽어도 결과 포트의 위치는 증가하지 않아요.
close?가 참이면 결과 포트를 닫을 때 in도 닫아요. close?가 #f이면 결과 포트를 닫아도 in은 닫히지 않아요.
name 인자는 결과 포트의 이름으로 쓰이며, 기본값은 in과 같은 이름을 유지해요.
(relocate-output-port out line column position [close?] #:name name) → output-port?
out : output-port?line : (or/c exact-positive-integer? #f)column : (or/c exact-nonnegative-integer? #f)position : exact-positive-integer?close? : any/c = #tname : (object-name out)
relocate-input-port와 같지만 출력 포트용이에요.
(transplant-input-port in get-location init-pos [close? count-lines!] #:name name) → input-port?
in : input-port?get-location : (or/c (-> (values (or/c exact-positive-integer? #f) (or/c exact-nonnegative-integer? #f) (or/c exact-positive-integer? #f))) #f)init-pos : exact-positive-integer?close? : any/c = #tcount-lines! : (-> any) = voidname : (object-name in)
relocate-input-port와 같지만, 줄 세기가 활성화되면 get-location을 통해 임의의 위치 정보를 만들 수 있어요. get-location은 make-input-port에서처럼 사용돼요. get-location이 #f이면 포트는 in이 보고하는 위치와 무관하게 init-pos에서 시작해 보통 방식으로 줄을 세요.
count-lines!이 제공되면 결과 포트에서 줄 세기가 활성화될 때 호출돼요. 기본값은 void예요.
(transplant-output-port out get-location init-pos [close? count-lines!] #:name name) → output-port?
out : output-port?get-location : (or/c (-> (values (or/c exact-positive-integer? #f) (or/c exact-nonnegative-integer? #f) (or/c exact-positive-integer? #f))) #f)init-pos : exact-positive-integer?close? : any/c = #tcount-lines! : (-> any) = voidname : (object-name out)
transplant-input-port와 같지만 출력 포트용이에요.
(filter-read-input-port in read-wrap peek-wrap [close?]) → input-port?
in : input-port?read-wrap : (bytes? (or/c exact-nonnegative-integer? eof-object? procedure? evt?) . -> . (or/c exact-nonnegative-integer? eof-object? procedure? evt?))peek-wrap : (bytes? exact-nonnegative-integer? (or/c evt? #f) (or/c exact-nonnegative-integer? eof-object? procedure? evt? #f) . -> . (or/c exact-nonnegative-integer? eof-object? procedure? evt? #f))close? : any/c = #t
in에서 끌어오지만, 포트의 읽기·peek 절차(make-input-port의 의미에서)의 각 결과를 read-wrap과 peek-wrap으로 필터링하는 포트를 만들어요. 필터링 절차들은 각 호출에서 in의 읽기·peek 절차의 인자와 결과를 모두 받아요.
close?가 참이면 결과 포트를 닫을 때 in도 닫아요.
(special-filter-input-port in proc [close?]) → input-port?
in : input-port?proc : (procedure? bytes? . -> . (or/c exact-nonnegative-integer? eof-object? procedure? evt?))close? : any/c = #t
in과 동등한 입력 포트를 만들되, in이 특수 값에 접근할 절차를 만들면 proc이 그 절차에 적용되어 특수 값을 대안으로 교체할 수 있게 해요. proc은 특수 값 절차와 포트의 읽기·peek 함수에 주어진 바이트 문자열(make-input-port 참고)과 함께 호출되고, 그 결과가 읽기·peek 함수의 결과로 사용돼요. proc은 바이트 문자열을 수정해 특수 값 대신 바이트를 대체할 수 있지만, 바이트 문자열은 최소 한 바이트를 담고 있다는 것만 보장돼요.
close?가 참이면 결과 입력 포트를 닫을 때 in도 닫아요.
포트 이벤트 (Port Events)
포트에서 읽기를 동기화 가능한 이벤트로 감싸 sync와 함께 사용하는 함수들이에요.
(eof-evt in) → evt?
in : input-port?
in이 eof를 만들면 준비되는 동기화 가능한 이벤트를 돌려줘요. in이 스트림 중간 eof를 만들면, 그 eof는 동기화에서 그 이벤트가 선택된 경우에만 이벤트가 소비해요.
동기화 시도 중 in에서 읽으려다 예외가 발생하면, 예외는 동기화 시도 중에 보고될 수 있지만, 같은 동기화의 다른 이벤트가 선택되거나 다른 이벤트가 먼저 예외를 일으키면 조용히 버려져요.
버전 7.5.0.3에서 변경: 읽기 오류 처리를 백그라운드 스레드의 처리되지 않은 오류로 취급하는 대신, 읽기 오류가 동기화 시도로 전파되도록 변경.
(read-bytes-evt k in) → evt?
k : exact-nonnegative-integer?in : input-port?
in에서 k 바이트를 읽거나 in에서 eof를 만나면 준비되는 동기화 가능한 이벤트를 돌려줘요. k가 0이면 이벤트는 ""로 즉시 준비돼요. 0이 아닌 k에 대해, eof 전에 바이트가 없으면 이벤트의 결과는 eof예요. 그렇지 않으면 이벤트의 결과는 사용 가능한 eof 전에 사용할 수 있는 만큼(k까지)의 바이트를 담은 최대 k 바이트의 바이트 문자열이에요. (결과가 k 바이트보다 적은 바이트 문자열인 것은 eof를 만난 경우에만 가능해요.)
바이트는 이벤트가 동기화에서 선택된 경우에만 포트에서 읽히고, 반환된 바이트는 항상 포트 스트림의 연속된 바이트를 나타내요.
이벤트는 여러 번 — 심지어 동시에 — 동기화할 수 있고, 각 동기화는 별개의 읽기 요청에 대응해요.
in은 progress 이벤트를 지원해야 하고, 읽기 시도 중에 특수한 비바이트 값을 만들어서는 안 돼요.
in에서 읽으려다 예외가 생기면 eof-evt처럼 처리돼요.
(read-bytes!-evt bstr in) → evt?
bstr : (and/c bytes? (not/c immutable?))in : input-port?
read-bytes-evt와 같지만, 읽은 바이트는 bstr에 담기고, 읽을 바이트 수는 (bytes-length bstr)에 대응해요. 이벤트의 결과는 eof이거나 읽은 바이트 수예요.
bstr은 이벤트에 대한 첫 동기화 시도 후부터, 이벤트가 선택되거나, #f가 아닌 progress-evt가 준비되거나, (동기화 시점의) 현재 custodian이 종료될 때까지 언제든 변형될 수 있어요. 만약 이벤트가 동기화로 선택되지 않으면 bstr이 언제 변형될지에 대한 시간 제한은 그 외에는 없음을 유의하세요. 그렇더라도, 동기화 시도 중 하나가 이벤트를 선택할 때까지 in에서의 개재 읽기가 없기만 하면, 여러 동기화 시도가 read-bytes!-evt의 같은 결과를 사용할 수 있어요.
in에서 읽으려다 예외가 생기면 eof-evt처럼 처리돼요.
(read-bytes-avail!-evt bstr in) → evt?
bstr : (and/c bytes? (not/c immutable?))in : input-port?
read-bytes!-evt와 같지만, 이벤트는 최소 한 바이트나 하나의 eof를 사용할 수 있게 된 후 즉시 사용 가능한 만큼의 바이트만 읽어요.
(read-string-evt k in) → evt?
k : exact-nonnegative-integer?in : input-port?
read-bytes-evt와 같지만 바이트 문자열 대신 문자 문자열용이에요.
(read-string!-evt str in) → evt?
str : (and/c string? (not/c immutable?))in : input-port?
read-bytes!-evt와 같지만 바이트 문자열 대신 문자 문자열용이에요.
(read-line-evt in [mode]) → evt?
in : input-port?mode : (or/c 'linefeed 'return 'return-linefeed 'any 'any-one) = 'linefeed
in에서 한 줄의 문자나 eof를 읽을 수 있으면 준비되는 동기화 가능한 이벤트를 돌려줘요. mode의 의미는 read-line과 같아요. 이벤트 결과는 읽은 문자 줄(줄 구분자 포함하지 않음)이에요.
줄은 이벤트가 동기화에서 선택된 경우에만 포트에서 읽히고, 반환된 줄은 항상 포트 스트림의 연속된 바이트를 나타내요.
in에서 읽으려다 예외가 생기면 eof-evt처럼 처리돼요.
(read-bytes-line-evt in [mode]) → evt?
in : input-port?mode : (or/c 'linefeed 'return 'return-linefeed 'any 'any-one) = 'linefeed
read-line-evt와 같지만 문자열 대신 바이트 문자열을 돌려줘요.
(peek-bytes-evt k skip progress-evt in) → evt?
k : exact-nonnegative-integer?skip : exact-nonnegative-integer?progress-evt : (or/c progress-evt? #f)in : input-port?
(peek-bytes!-evt bstr skip progress-evt in) → evt?
bstr : (and/c bytes? (not/c immutable?))skip : exact-nonnegative-integer?progress-evt : (or/c progress-evt? #f)in : input-port?
(peek-bytes-avail!-evt bstr skip progress-evt in) → evt?
bstr : (and/c bytes? (not/c immutable?))skip : exact-nonnegative-integer?progress-evt : (or/c progress-evt? #f)in : input-port?
(peek-string-evt k skip progress-evt in) → evt?
k : exact-nonnegative-integer?skip : exact-nonnegative-integer?progress-evt : (or/c progress-evt? #f)in : input-port?
(peek-string!-evt str skip progress-evt in) → evt?
str : (and/c string? (not/c immutable?))skip : exact-nonnegative-integer?progress-evt : (or/c progress-evt? #f)in : input-port?
read-bytes-evt 등의 함수들과 같지만 peek용이에요. skip 인자는 건너뛸 바이트 수를 나타내고, progress-evt는 peek을 실질적으로 취소하는 이벤트를 나타내요(즉 이벤트가 결코 준비되지 않게 해요). progress-evt 인자는 #f일 수 있는데, 그 경우 이벤트는 결코 취소되지 않아요.
(regexp-match-evt pattern in) → any
pattern : (or/c string? bytes? regexp? byte-regexp?)in : input-port?
pattern이 in의 바이트/문자 스트림과 매치되면 준비되는 동기화 가능한 이벤트를 돌려줘요; regexp-match도 참고하세요. 이벤트의 값은 매치 결과로, regexp-match의 결과와 같은 형태예요.
pattern이 스트림 시작 매치를 요구하지 않으면, 매치를 완료하기 위해 건너뛴 바이트는 이벤트가 동기화에서 선택될 때 읽혀서 버려져요.
바이트는 이벤트가 동기화에서 선택된 경우에만 포트에서 읽히고, 반환된 매치는 항상 포트 스트림의 연속된 바이트를 나타내요. 아직 사용할 수 없는 포트 바이트가 매치에 기여할 수 있으면 이벤트는 준비되지 않아요. 마찬가지로, pattern이 스트림 시작 ^로 시작하는데 처음에 매치되지 않으면, 포트에서 바이트가 읽힐 때까지 이벤트가 준비될 수 없어요.
이벤트는 여러 번 — 심지어 동시에 — 동기화할 수 있고, 각 동기화는 별개의 매치 요청에 대응해요.
in 포트는 progress 이벤트를 지원해야 해요. 매치 시도 중 in이 특수한 비바이트 값을 반환하면 eof처럼 취급돼요.
in에서 읽으려다 예외가 생기면 eof-evt처럼 처리돼요.
스트림 복사 (Copying Streams)
한 스트림에서 다른 스트림으로 데이터를 옮기는 함수들이에요.
(convert-stream from-encoding in to-encoding out) → void?
from-encoding : string?in : input-port?to-encoding : string?out : output-port?
in에서 데이터를 읽어 (bytes-open-converter from-encoding to-encoding)로 변환하고 그 변환된 바이트를 out에 써요. convert-stream 절차는 in에서 eof에 도달한 후 반환돼요.
변환기를 여는 데 실패하면 exn:fail 예외가 일어나요. 마찬가지로 in에서 읽는 동안 어느 시점이든 변환 오류가 발생하면 exn:fail 예외가 일어나요.
(copy-port in out ...+) → void?
in : input-port?out : output-port?
in에서 데이터를 읽어 out에 다시 써주고, in이 eof를 만들면 반환해요. 복사는 효율적이고 상당한 버퍼 지연 없이 이뤄져요(즉, in에서 사용 가능해진 바이트는 in에서 이후 읽기가 차단돼야 하더라도 즉시 out으로 옮겨져요). in이 특수한 비바이트 값을 만들면 write-special로 out에 전달돼요.
이 함수는 한 스트림에서 다른 스트림으로 데이터를 계속 흘려보내기 위해 "백그라운드" 스레드에서 자주 호출돼요.
여러 out이 제공되면, in의 데이터는 모든 out에 쓰여요. 서로 다른 out들은 서로의 출력을 차단해요. in에서 읽은 각 데이터 블록은 다음 out으로 넘어가기 전에 하나의 out에 완전히 쓰여지기 때문이에요. out들은 제공된 순서대로 쓰이므로, 비차단 포트(예: 파일 출력 포트)는 인자 목록의 앞에 두는 게 좋아요.