IO::CatHandle — 여러 IO 핸들을 하나처럼 다루기

IO::CatHandle — 여러 IO 핸들을 하나처럼 다루기

여러 파일을 한 번에, 그것도 마치 하나의 핸들인 것처럼 차례로 읽고 싶은 경우가 있어요. IO::CatHandle 클래스가 그 일을 해줘요. 여러 IO::HandleIO::Pipe 소스로부터 입력을 매끄럽게 모아들이는 IO::Handle을 만들 수단이죠. 이 클래스는 Rakudo 2017.06 릴리스부터 사용할 수 있어요.

class IO::CatHandle is IO::Handle { }

IO::Handle의 메서드가 모두 구현되어 있어요. 다만 쓰기 메서드를 쓰려 하면 (현재는) 예외를 던지죠. 그래서 IO::CatHandle은 읽기 전용 IO::Handle을 쓸 수 있는 모든 곳에서 사용할 수 있어요.

출처: Raku Documentation — IO::CatHandle

본문

메서드 (Methods)

method new

method new(*@handles, :&on-switch, :$chomp = True,
           :$nl-in = ["\n", "\r\n"], Str :$encoding, Bool :$bin)

IO::CatHandle 객체를 만들어요.

@handles 위치 인자는 IO::CatHandle이 읽을 핸들 소스를 나타내며, Cool, IO::Path, IO::Handle(의 IO::Pipe 포함) 객체들이 섞여 담길 수 있어요. IO::CatHandle의 입력이 처리될 때(.new 호출 중이 아니라 @handles의 데이터가 필요할 때만 동작) @handles 목록을 차례로 걸어가며 각 인자를 다음과 같이 처리해요.

  • Cool 객체는 IO::Path로 강제 변환돼요.
  • IO::Path 객체는 IO::CatHandle(호출 객체)의 속성을 사용해 open 호출 시 읽기용으로 열려요.
  • 아직 열리지 않은 IO::Handle 객체는 IO::Path 객체와 같은 방식으로 열려요.
  • 이미 열린 IO::Handle 객체는 모든 속성이 호출 객체 IO::CatHandle의 속성으로 설정돼요.

요약하면 모든 @handles가 결국 호출 객체 IO::CatHandle과 같은 모드·같은 속성으로 열린 IO::Handle 객체가 돼요.

:&on-switch 명명 인자에 대한 자세한 내용은 .on-switch 메서드를 참고하세요. 기본값으로는 설정되지 않아요.

:encoding 명명 인자는 핸들의 인코딩을 지정하며 IO::Handle.encoding이 받는 것과 같은 값을 받아요. 핸들을 이진 모드로 만들고 싶으면 :bin 명명 인자를 True로 설정하세요. 정의된 :encodingTrue:bin을 동시에 지정하려 하면 치명적 오류로 X::IO::BinaryAndEncoding 예외가 던져져요. 둘 다 지정하지 않으면 핸들의 기본 인코딩은 utf8이 돼요.

:chomp:nl-in 인자는 IO::Handle에서와 같은 의미를 가지며 같은 값을 받고 기본값도 같아요.

method chomp

method chomp(IO::CatHandle:D:) is rw

호출 객체의 $.chomp 속성을 할당된 값으로 설정해요. 활성 핸들을 포함한 모든 소스 핸들이 제공된 $.chomp 값을 사용해요.

(my $f1 = 'foo'.IO).spurt: "A\nB\nC\n";
(my $f2 = 'bar'.IO).spurt: "D\nE\n";
with IO::CatHandle.new: $f1, $f2 {
    # .chomp is True by default:
    (.get xx 2).raku.say; # OUTPUT: «("A", "B").Seq␤»

    .chomp = False;
    (.get xx 3).raku.say; # OUTPUT: «("C\n", "D\n", "E\n").Seq␤»
    .close
}

method nl-in

method nl-in(IO::CatHandle:D:) is rw

호출 객체의 $.nl-in 속성을 할당된 값으로 설정해요. 이 값은 Str이거나 StrList일 수 있으며, 각 Str 객체가 한 줄의 끝을 나타내는 문자열이에요. 활성 핸들을 포함한 모든 소스 핸들이 제공된 $.nl-in 값을 사용해요. 소스 핸들 사이의 경계는 항상 새 줄바꿈으로 계산된다는 점을 기억하세요.

(my $f1 = 'foo'.IO).spurt: "A\nB\nC";
(my $f2 = 'bar'.IO).spurt: "DxEx";
with IO::CatHandle.new: $f1, $f2 {
    # .nl-in is ["\n", "\r\n"] by default:
    (.get xx 2).raku.say; # OUTPUT: «("A", "B").Seq␤»

    .nl-in = 'x';
    (.get xx 3).raku.say; # OUTPUT: «("C", "D", "E").Seq␤»
    .close
}

method close

method close(IO::CatHandle:D: --> True)

현재 활성 소스 핸들과 이미 열려 있는 모든 소스 핸들을 닫고, 소스 핸들 큐를 비워요. 일반 IO::Handle과 달리 IO::CatHandle에서는 close를 명시적으로 호출할 필요가 없는 경우가 많아요. 모든 입력을 소진하는 것만으로 닫아야 할 핸들을 전부 닫아주기 때문이죠.

with IO::CatHandle.new: @bunch-of-handles {
    say .readchars: 42;
    .close; # we are done; close all the open handles
}

method comb

method comb(IO::CatHandle:D: |args --> Seq:D)

핸들을 읽고 Str.comb가 하는 것과 같은 방식으로 내용을 처리해요. 같은 인자를 받죠. 이 메서드를 호출할 때 구현이 모든 소스 핸들의 내용을 통째로 slurp할 수도 있어요.

(my $f1 = 'foo'.IO).spurt: 'foo';
(my $f2 = 'bar'.IO).spurt: 'bar';
IO::CatHandle.new($f1, $f2).comb(2).raku.say;
# OUTPUT: «("fo", "ob", "ar").Seq␤»

method DESTROY

method DESTROY(IO::CatHandle:D:)

.close를 호출해요. 이 메서드가 직접 쓰이지는 않고, 가비지 컬렉션 과정에서 호출돼요.

method encoding

multi method encoding(IO::CatHandle:D:)
multi method encoding(IO::CatHandle:D: $new-encoding)

호출 객체의 $.encoding 속성을 제공된 값으로 설정해요. 유효한 값은 IO::Handle.encoding이 받는 것과 같아요(이진 모드로 전환하려면 Nil 값을 사용하세요). 활성 핸들을 포함한 모든 소스 핸들이 제공된 $.encoding 값을 사용해요.

(my $f1 = 'foo'.IO).spurt: 'I ♥ Raku';
(my $f2 = 'bar'.IO).spurt: 'meow';
with IO::CatHandle.new: $f1, $f2 {
    # .encoding is 'utf8' by default:
    .readchars(5).say; # OUTPUT: «I ♥ R␤»

    .encoding: Nil; # switch to binary mode
    .slurp.say; # OUTPUT: «Buf[uint8]:0x<6B 75 6D 65 6F 77>␤»
}

method eof

method eof(IO::CatHandle:D: --> Bool:D)

읽기 연산이 마지막 핸들의 내용을 포함해 소스 핸들 큐를 소진했으면 True를 반환해요. 참고: 이 메서드를 호출하면, 소스 핸들 큐를 검사하는 동안 한 번 이상 .on-switch 호출이 발생할 수 있고, 소스 핸들 큐가 소진될 수도 있어요.

(my $f1 = 'foo'.IO).spurt: 'foo';
(my $f2 = 'bar'.IO).spurt: 'bar';
with IO::CatHandle.new: :on-switch{ print 'SWITCH! ' }, $f1, $f2 {
                   # OUTPUT: «SWITCH! »
    .eof.say;      # OUTPUT: «False␤»
    .readchars(3);
    .eof.say;      # OUTPUT: «SWITCH! False␤»

    .slurp;        # OUTPUT: «SWITCH! »
    .eof.say;      # OUTPUT: «True␤»
}

seek할 수 없는 핸들과 빈 파일에 적용되는 IO::Handle.eof의 주의점이 여기서도 동일하게 적용돼요.

method get

method get(IO::CatHandle:D: --> Bool:D)

핸들에서 입력 한 줄을 반환해요. 새 줄 문자열은 $.nl-in 속성의 값으로 정의되며, $.chomp 속성이 True로 설정돼 있으면 그 줄에서 제거돼요. 더 이상 입력이 없으면 Nil을 반환해요. 핸들이 이진 모드일 때 이 메서드를 호출하면 오류로, X::IO::BinaryMode 예외가 던져져요.

(my $f1 = 'foo'.IO).spurt: "a\nb\nc";
(my $f2 = 'bar'.IO).spurt: "d\ne";
my $cat = IO::CatHandle.new: $f1, $f2;
.say while $_ = $cat.get; # OUTPUT: «a␤b␤c␤d␤e␤»

method getc

method getc(IO::CatHandle:D: --> Bool:D)

핸들에서 입력 한 문자를 반환해요. IO::Handle.getc에서 설명한 주의점이 모두 적용돼요. 더 이상 입력이 없으면 Nil을 반환해요. 핸들이 이진 모드일 때 이 메서드를 호출하면 오류로, X::IO::BinaryMode 예외가 던져져요.

(my $f1 = 'foo'.IO).spurt: 'I ♥ Raku';
(my $f2 = 'bar'.IO).spurt: 'meow';
my $cat = IO::CatHandle.new: $f1, $f2;
.say while $_ = $cat.getc; # OUTPUT: «I␤ ␤♥␤ ␤R␤a␤k␤u␤m␤e␤o␤w␤»

method handles

method handles(IO::CatHandle:D: --> Seq:D)

현재 활성 핸들과, next-handle를 호출해 만들어지는 나머지 소스 핸들들을 모두 담은 Seq를 반환해요. 호출 객체가 이미 완전히 소비됐다면 빈 Seq를 반환해요.

이 메서드는 IO::ArgFiles에서 특히 유용해요. 각 파일핸들을 별도로 다루고 싶을 때가 많기 때문이죠.

# print at most the first 2 lines of each file in $*ARGFILES:
.say for flat $*ARGFILES.handles.map: *.lines: 2

이 메서드를 여러 번 호출하는 것은 허용돼요. .handles.head는 현재 활성 핸들을 얻는 유효한 관용구예요. 반환된 Seq의 요소를 reify하는 사이에 핸들이 다른 수단으로 전환되면, Seq가 만들어내는 다음 요소는 호출 객체의 다음 핸들이 돼요. 전환이 없었더라면 만들어졌을 핸들이 아니라요.

(my $file1 := 'file1'.IO).spurt: "1a\n1b\n1c";
(my $file2 := 'file2'.IO).spurt: "2a\n2b\n2c";
(my $file3 := 'file3'.IO).spurt: "3a\n3b\n3c";
my $cat := IO::CatHandle.new: $file1, $file2, $file3;
for $cat.handles {
    say .lines: 2;
    $cat.next-handle;
}
# OUTPUT: «(1a 1b)␤(3a 3b)␤»

마찬가지로 반환된 Seq를 reify하면 호출 객체의 소스 핸들이 소비되고, 완전히 reify되면 호출 객체는 완전히 소비된 상태가 돼요.

method IO

method IO(IO::CatHandle:D:)

.path의 별칭이에요.

method lines

method lines(IO::CatHandle:D: $limit = Inf, :$close --> Seq:D)

IO::Handle.lines와 같아요. 다만 소스 핸들 사이의 경계가 새 줄바꿈으로 간주된다는 점을 기억하세요.

(my $f1 = 'foo'.IO).spurt: "foo\nbar";
(my $f2 = 'bar'.IO).spurt: 'meow';
IO::CatHandle.new($f1, $f2).lines.raku.say;
# OUTPUT: «("foo", "bar", "meow").Seq␤»

참고: :closeFalse여도 완전히 소비된 핸들은 여전히 닫혀요.

method lock

method lock(IO::CatHandle:D: Bool:D :$non-blocking = False, Bool:D :$shared = False --> True)

IO::Handle.lock과 같아요. 소스 핸들 큐가 소진됐으면 Nil을 반환해요.

현재 활성 소스 핸들만 잠가요. .on-switch Callable을 쓰면 CatHandle이 처리하는 핸들을 편리하게 잠그고 풀 수 있어요.

method native-descriptor

method native-descriptor(IO::CatHandle:D: --> Int:D)

현재 활성 소스 핸들의 native-descriptor를 반환하거나, 소스 핸들 큐가 소진됐으면 Nil을 반환해요.

CatHandle은 소스 핸들을 다 쓰면 닫아버리므로, 연속된 소스 핸들이 .newCool이나 IO::Path 객체로 전달됐다면 같은 native descriptor를 가질 수 있어요.

(my $f1 = 'foo'.IO).spurt: 'foo';
(my $f2 = 'bar'.IO).spurt: 'bar';
with IO::CatHandle.new: $f1, $f2, $*IN {
    repeat { .native-descriptor.say } while .next-handle;
    # OUTPUT: «13␤13␤9␤»
}

method next-handle

method next-handle(IO::CatHandle:D: --> IO::Handle:D)

활성 소스 핸들을 소스 핸들 큐의 다음 핸들로 전환해요. 여기서 큐란 .new@handles 속성에 주어진 소스들이에요. 반환값은 현재 활성 소스 핸들이며, 큐가 소진됐으면 Nil이에요.

Cool 소스 "핸들"은 IO::Path로 강제 변환돼요. IO::Path와 열리지 않은 IO::Handle 소스 핸들은 호출 객체의 $.nl-in, $.chomp, $.encoding 속성을 사용해 읽기용으로 열리고, 이미 열린 IO::Handle 객체의 같은 속성들은 호출 객체의 속성 값으로 바뀌어요.

이 메서드는 CatHandle의 메서드가 다음 소스 핸들로의 전환을 필요로 할 때마다 자동으로 호출되고, .on-switch Callable을 트리거하며, .new 호출 중에 한 번 호출돼요. .on-switch는 소스 핸들 큐가 소진된 후에도 이 메서드가 호출될 때마다 계속 트리거돼요. 일반적으로 현재 활성 소스 핸들의 EOF에 도달하는 것 자체가 .next-handle 호출을 트리거하지는 않고, 더 많은 데이터가 필요한 추가 읽기 연산이 트리거해요.

(my $f1 = 'foo'.IO).spurt: "a\nb";
(my $f2 = 'bar'.IO).spurt: "c\nd";
with IO::CatHandle.new: :on-switch{ say '▸ Switching' }, $f1, $f2 {
    say 'one';
    .next-handle.^name.say;
    say 'two';
    .next-handle.^name.say;
    say 'three';
    .next-handle.^name.say;
    # OUTPUT:
    # ▸ Switching
    # one
    # ▸ Switching
    # IO::Handle
    # two
    # ▸ Switching
    # Nil
    # three
    # ▸ Switching
    # Nil
}

method on-switch

has &.on-switch is rw

.new 호출 중 설정할 수 있고 나중에 할당으로 바꿀 수도 있는 속성 중 하나예요. 기본적으로는 지정되지 않아요. .count0, 1, 2, 또는 InfCallable을 받아요. .next-handle가 호출될 때마다 호출되는데, 이는 .new 호출 중 한 번, 그리고 소스 핸들이 큐의 다음으로 전환될 때마다, 또는 .next-handle 메서드를 직접 호출할 때마다 발생해요.

&.on-switch.count0이면 인자를 받지 않고, 1이면 현재 활성 핸들을 받으며, 2 또는 Inf면 현재 활성 핸들과 마지막 활성 핸들을 위치 인자로 (그 순서대로) 받아요. 맨 처음 &.on-switch 실행에서는 "마지막 활성 핸들" 인자가 Nil이에요. 소스 핸들 큐가 소진되면 "현재 활성 핸들" 인자가 Nil이 되고, 그 이후의 모든 실행에서는 두 인자가 모두 Nil이 돼요.

(my $f1 = 'foo'.IO).spurt: "A\nB\nC";
(my $f2 = 'bar'.IO).spurt: "D\nE";

my $line;
my $cat = IO::CatHandle.new: :on-switch{ $line = 1 }, $f1, $f2;
say "{$cat.path}:{$line++} $_" for $cat.lines;
# OUTPUT:
# foo:1 A
# foo:2 B
# foo:3 C
# bar:1 D
# bar:2 E
my @old-stuff;
sub on-switch ($new, $old) {
    $new and $new.seek: 1, SeekFromBeginning;
    $old and @old-stuff.push: $old.open.slurp: :close;
}

(my $f1 = 'foo'.IO).spurt: "A\nB\nC";
(my $f2 = 'bar'.IO).spurt: "D\nE";
my $cat = IO::CatHandle.new: :&on-switch, $f1, $f2;
$cat.lines.raku.say; # OUTPUT: «("", "B", "C", "", "E").Seq␤»
@old-stuff.raku.say; # OUTPUT: «["A\nB\nC", "D\nE"]␤»

method open

method open(IO::CatHandle:D: --> IO::CatHandle:D)

호출 객체를 반환해요. 이 메서드의 의도는 IO::Handle을 여는 것들과 CatHandle을 함께 동작하게 만들기 위함이에요. 의도적으로 호출할 필요는 없어요.

method opened

method opened(IO::CatHandle:D: --> Bool:D)

호출 객체에 소스 핸들이 하나라도 있으면 True, 그렇지 않으면 False를 반환해요.

say IO::CatHandle.new      .opened; # OUTPUT: «False␤»
say IO::CatHandle.new($*IN).opened; # OUTPUT: «True␤»

(my $f1 = 'foo'.IO).spurt: "A\nB\nC";
with IO::CatHandle.new: $f1 {
    .opened.say; # OUTPUT: «True␤»
    .slurp;
    .opened.say; # OUTPUT: «False␤»
}

method path

method path(IO::CatHandle:D:)

현재 활성 소스 핸들의 .path 속성 값을 반환하거나, 소스 핸들 큐가 소진됐으면 Nil을 반환해요. 기본적으로 CatHandle이 파일에 기반하고 있다면, CatHandle이 현재 읽고 있는 파일의 경로를 얻는 방법이 바로 이거예요.

(my $f1 = 'foo'.IO).spurt: "A\nB\nC";
(my $f2 = 'bar'.IO).spurt: "D\nE";

my $line;
my $cat = IO::CatHandle.new: :on-switch{ $line = 1 }, $f1, $f2;
say "{$cat.path}:{$line++} $_" for $cat.lines;
# OUTPUT:
# foo:1 A
# foo:2 B
# foo:3 C
# bar:1 D
# bar:2 E

method read

method read(IO::CatHandle:D: Int(Cool:D) $bytes = 65536 --> Buf:D)

핸들에서 최대 $bytes 바이트를 읽어 Buf로 반환해요. $bytes는 구현별 값을 기본값으로 가지며(Rakudo에서는 $*DEFAULT-READ-ELEMS 값, 기본값은 65536), 이 메서드는 이진 모드가 아닌 핸들에서도 호출할 수 있어요.

(my $f1 = 'foo'.IO).spurt: 'meow';
(my $f2 = 'bar'.IO).spurt: Blob.new: 4, 5, 6;
with IO::CatHandle.new: :bin, $f1, $f2 {
    say .read: 2;    # OUTPUT: «Buf[uint8]:0x<6d 65>␤»
    say .read: 2000; # OUTPUT: «Buf[uint8]:0x<6f 77 04 05 06>␤»
}

# Non-binary mode is OK too:
with IO::CatHandle.new: $f1, $f2 {
    say .get;        # OUTPUT: «meow␤»
    say .read: 2000; # OUTPUT: «Buf[uint8]:0x<04 05 06>␤»
}

method readchars

method readchars(IO::CatHandle:D: Int(Cool:D) $chars = 65536 --> Str:D)

핸들에서 읽은 최대 $chars 문자의 Str을 반환해요. $chars는 구현별 값을 기본값으로 가지며(Rakudo에서는 $*DEFAULT-READ-ELEMS 값, 기본값은 65536), 이진 모드로 열린 핸들에서는 이 메서드를 호출할 수 없으며, 호출하면 X::IO::BinaryMode 예외가 던져져요.

(my $f1 = 'foo'.IO).spurt: 'Raku loves to';
(my $f2 = 'bar'.IO).spurt: ' meow';

with IO::CatHandle.new: $f1, $f2 {
    say .readchars: 11;   # OUTPUT: «Raku loves ␤»
    say .readchars: 1000; # OUTPUT: «to meow␤»
}

method seek

method seek(IO::CatHandle:D: |c)

현재 활성 소스 핸들에 .seek를 호출하고 모든 인자를 전달한 뒤 그 결과를 반환해요. 소스 핸들 큐가 소진됐으면 Nil을 반환해요. 참고: 이 메서드는 소스 핸들 전환을 수행하지 않아요. 따라서 현재 소스 핸들의 끝을 넘어 seek해도 큐의 다음 소스 핸들로 seek되지 않고, 현재 소스 핸들의 시작을 넘어 seek하는 것은 치명적 오류예요. 소스 핸들이 언제 전환되는지에 대한 자세한 내용은 .next-handle도 함께 보세요.

(my $f1 = 'foo'.IO).spurt: 'foo';
(my $f2 = 'bar'.IO).spurt: 'bar';

with IO::CatHandle.new: $f1, $f2 {
    .get.say;                     # OUTPUT: «foo␤»
    .seek: -2, SeekFromCurrent;
    .readchars(2).say;            # OUTPUT: «oo␤»
    .seek: 1000, SeekFromCurrent; # this doesn't switch to second handle!
    .readchars(3).say;            # OUTPUT: «bar␤»
    try .seek: -4;                # this won't seek to previous handle!
    say ~$!;                      # OUTPUT: «Failed to seek in filehandle: 22␤»
}

method tell

method tell(IO::CatHandle:D: --> Int:D)

현재 활성 소스 핸들에 .tell을 호출하고 그 결과를 반환해요. 소스 핸들 큐가 소진됐으면 Nil을 반환해요.

(my $f1 = 'foo'.IO).spurt: 'foo';
(my $f2 = 'bar'.IO).spurt: 'bar';

with IO::CatHandle.new: $f1, $f2 {
    .get.say;                   # OUTPUT: «foo␤»
    .tell.say;                  # OUTPUT: «3␤»
    .seek: -2, SeekFromCurrent;
    .tell.say;                  # OUTPUT: «1␤»
    say .readchars: 3;          # OUTPUT: «oob␤»
    .tell.say;                  # OUTPUT: «2␤»
    }

method slurp

method slurp(IO::CatHandle:D:)

모든 소스 핸들에서 사용 가능한 입력 전체를 읽어, 핸들이 이진 모드면 Buf로, 아니면 Str로 반환해요. 소스 핸들 큐가 소진됐으면 Nil을 반환해요.

(my $f1 = 'foo'.IO).spurt: 'foo';
(my $f2 = 'bar'.IO).spurt: 'bar';

IO::CatHandle.new(      $f1, $f2).slurp.say; # OUTPUT: «foobar␤»
IO::CatHandle.new(:bin, $f1, $f2).slurp.say; # OUTPUT: «Buf[uint8]:0x<66 6f 6f 62 61 72>␤»
IO::CatHandle.new                .slurp.say; # OUTPUT: «Nil␤»

method split

method split(IO::CatHandle:D: |args --> Seq:D)

핸들을 읽고 Str.split이 하는 것과 같은 방식으로 내용을 처리해요. 같은 인자를 받죠. 이 메서드를 호출할 때 구현이 모든 소스 핸들의 내용을 통째로 slurp할 수도 있어요.

(my $f1 = 'foo'.IO).spurt: 'foo';
(my $f2 = 'bar'.IO).spurt: 'bar';
IO::CatHandle.new($f1, $f2).split(/o+/).raku.say;
# OUTPUT: «("f", "bar").Seq␤»

method Str

method Str(IO::CatHandle:D: --> Str:D)

현재 활성 소스 핸들에 .Str을 호출하고 그 결과를 반환해요. 소스 핸들 큐가 소진됐다면 구현 정의 문자열(Rakudo에서는 '<closed IO::CatHandle>')을 반환해요.

method Supply

method Supply(IO::CatHandle:D: :$size = 65536 --> Supply:D)

.read(핸들이 이진 모드일 때) 또는 .readchars(그렇지 않을 때)로 공급되는 Supply를 반환해요. 읽기는 :$size 바이트 또는 문자 단위로 이뤄져요. :$size는 구현별 값을 기본값으로 가지며(Rakudo에서는 $*DEFAULT-READ-ELEMS 값, 기본값은 65536)요.

(my $f1 = 'foo'.IO).spurt: 'foo';
(my $f2 = 'bar'.IO).spurt: 'bar';
react whenever IO::CatHandle.new($f1, $f2).Supply: :2size {.say}
# OUTPUT: «fo␤ob␤ar␤»

react whenever IO::CatHandle.new(:bin, $f1, $f2).Supply: :2size {.say}
# OUTPUT: «Buf[uint8]:0x<66 6f>␤Buf[uint8]:0x<6f 62>␤Buf[uint8]:0x<61 72>␤»

method t

method t(IO::CatHandle:D: --> Bool:D)

현재 활성 소스 핸들에, 핸들이 TTY인지 알려주는 .t를 호출하고 그 결과를 반환해요. 소스 핸들 큐가 소진됐으면 False를 반환해요.

(my $f1 = 'foo'.IO).spurt: 'foo';
with IO::CatHandle.new: $f1, $*IN {
    repeat { .t.say } while .next-handle; # OUTPUT: «False␤True␤»
}

method unlock

method unlock(IO::CatHandle:D:)

IO::Handle.unlock과 같아요. 소스 핸들 큐가 소진됐으면 Nil을 반환해요.

현재 활성 소스 핸들만 잠금 해제해요. .on-switch Callable을 쓰면 CatHandle이 처리하는 핸들을 편리하게 잠그고 풀 수 있어요.

method words

method words(IO::CatHandle:D: $limit = Inf, :$close --> Seq:D)

IO::Handle.words와 같아요(어떤 수의 단어를 만드는 데 필요한 것보다 더 많은 데이터를 읽는 것에 대한 주의점도 포함해요). 소스 핸들 사이의 경계가 단어 경계로 간주된다는 점을 기억하세요.

(my $f1 = 'foo'.IO).spurt: 'foo bar';
(my $f2 = 'bar'.IO).spurt: 'meow';
IO::CatHandle.new($f1, $f2).words.raku.say;
# OUTPUT: «("foo", "bar", "meow").Seq␤»

참고: :closeFalse여도 완전히 소비된 핸들은 여전히 닫혀요.