use Fcntl ':seek'; use Test::More qw(no_plan); sub search { my ($fh, $cmp) = @_; my ($lo, $hi) = (0, -s $fh); while (1) { my $mid = int(($lo + $hi)/2); if ($mid) { seek $fh, $mid-1, SEEK_SET; my $junk = <$fh>; } else { seek $fh , $mid, SEEK_SET; } my $start = tell $fh; my $rec = <$fh>; return unless defined $rec; chomp($rec); if ($hi == $lo) { seek $fh, $start, SEEK_SET; return $rec }; local $_ = $rec; if ($cmp->($rec) < 0) { $lo = $mid+1 } else { $hi = $mid } } } sub lookfor { my $key = shift; sub { lc $_ cmp $key } } open FH, "<", "/usr/dict/words" or die $!; is(search(\*FH, lookfor('snonk')), 'snook'); is(search(\*FH, lookfor('snob')), 'snob'); is(search(\*FH, lookfor('snoba')), 'snobbery'); is(search(\*FH, lookfor('snobb')), 'snobbery'); is(search(\*FH, lookfor('snobbe')), 'snobbery'); is(search(\*FH, lookfor('snobber')), 'snobbery'); is(search(\*FH, lookfor('snobbery')), 'snobbery'); is(search(\*FH, lookfor('snobberys')), 'snobbish'); is(search(\*FH, lookfor('')), '10th'); is(search(\*FH, lookfor('1')), '10th'); is(search(\*FH, lookfor('11')), '1st'); is(search(\*FH, lookfor('zy')), 'zygote'); is(search(\*FH, lookfor('zz')), undef);