Class | Scanf::IO
In: | scanf.rb
Parent: | Object

### Methods

scanf

### Public Instance methods

scanf(str,&b)

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]

[Source]

    # File 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

[Validate]