Class: IO

Inherits:
Object
  • Object
show all
Defined in:
lib/scanf.rb

Instance Method Summary collapse

Instance Method Details

#scanf(str, &b) ⇒ Object

The trick here is doing a match where you grab one line of input at a time. The linebreak may or may not occur at the boundary where the string matches a format specifier. And if it does, some rule about whitespace may or may not be in effect...

That's why this is much more elaborate than the string version.

For each line: Match succeeds (non-emptily) and the last attempted spec/string sub-match succeeded:

could the last spec keep matching?
  yes: save interim results and continue (next line)

The last attempted spec/string did not match:

are we on the next-to-last spec in the string?

yes:
  is fmt_string.string_left all spaces?
    yes: does current spec care about input space?
      yes: fatal failure
      no: save interim results and continue
no: continue  [this state could be analyzed further]


590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
# File 'lib/scanf.rb', line 590

def scanf(str,&b)
  return block_scanf(str,&b) if b
  return [] unless str.size > 0

  start_position = pos rescue 0
  matched_so_far = 0
  source_buffer = ""
  result_buffer = []
  final_result = []

  fstr = Scanf::FormatString.new(str)

  loop do
    if eof || (tty? &&! fstr.match(source_buffer))
      final_result.concat(result_buffer)
      break
    end

    source_buffer << gets

    current_match = fstr.match(source_buffer)

    spec = fstr.last_spec_tried

    if spec.matched
      if spec.mid_match?
        result_buffer.replace(current_match)
        next
      end

    elsif (fstr.matched_count == fstr.spec_count - 1)
      if /\A\s*\z/.match(fstr.string_left)
        break if spec.count_space?
        result_buffer.replace(current_match)
        next
      end
    end

    final_result.concat(current_match)

    matched_so_far += source_buffer.size
    source_buffer.replace(fstr.string_left)
    matched_so_far -= source_buffer.size
    break if fstr.last_spec
    fstr.prune
  end
  seek(start_position + matched_so_far, IO::SEEK_SET) rescue Errno::ESPIPE
  soak_up_spaces if fstr.last_spec && fstr.space

  return final_result
end