mirror of
https://github.com/ruby/ruby.git
synced 2022-11-09 12:17:21 -05:00
Removed Scanf from the ruby repository.
This commit is contained in:
parent
a3b8501614
commit
67a6662032
18 changed files with 2 additions and 1388 deletions
1
NEWS
1
NEWS
|
@ -329,6 +329,7 @@ RubyGems::
|
|||
|
||||
* Removed unmaintained libraries.
|
||||
* CMath
|
||||
* Scanf
|
||||
|
||||
=== Stdlib compatibility issues (excluding feature bug fixes)
|
||||
|
||||
|
|
|
@ -220,9 +220,6 @@ Zachary Scott (zzak)
|
|||
[lib/rss.rb, lib/rss/*]
|
||||
Kouhei Sutou (kou)
|
||||
https://github.com/ruby/rss
|
||||
[lib/scanf.rb]
|
||||
David A. Black (dblack)
|
||||
https://github.com/ruby/scanf
|
||||
[lib/shell.rb, lib/shell/*]
|
||||
Keiju ISHITSUKA (keiju)
|
||||
https://github.com/ruby/shell
|
||||
|
|
|
@ -87,7 +87,6 @@ Racc:: A LALR(1) parser generator written in Ruby.
|
|||
RDoc:: Produces HTML and command-line documentation for Ruby
|
||||
REXML:: An XML toolkit for Ruby
|
||||
RSS:: Family of libraries that support various formats of XML "feeds"
|
||||
Scanf:: A Ruby implementation of the C function scanf(3)
|
||||
Shell:: An idiomatic Ruby interface for common UNIX shell commands
|
||||
Synchronizer:: A module that provides a two-phase lock with a counter
|
||||
ThreadsWait:: Watches for termination of multiple threads
|
||||
|
|
|
@ -1,24 +0,0 @@
|
|||
# coding: utf-8
|
||||
# frozen_string_literal: true
|
||||
|
||||
Gem::Specification.new do |spec|
|
||||
spec.name = "scanf"
|
||||
spec.version = "1.0.0"
|
||||
spec.authors = ["David Alan Black"]
|
||||
spec.email = ['dblack@superlink.net']
|
||||
|
||||
spec.summary = "scanf is an implementation of the C function scanf(3)."
|
||||
spec.description = "scanf is an implementation of the C function scanf(3)."
|
||||
spec.homepage = "https://github.com/ruby/scanf"
|
||||
spec.license = "BSD-2-Clause"
|
||||
|
||||
spec.files = ["lib/scanf.rb"]
|
||||
spec.bindir = "exe"
|
||||
spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
|
||||
spec.require_paths = ["lib"]
|
||||
spec.required_ruby_version = ">= 2.3.0"
|
||||
|
||||
spec.add_development_dependency "bundler", "~> 1.14"
|
||||
spec.add_development_dependency "rake", "~> 10.0"
|
||||
spec.add_development_dependency "test-unit"
|
||||
end
|
776
lib/scanf.rb
776
lib/scanf.rb
|
@ -1,776 +0,0 @@
|
|||
# frozen_string_literal: false
|
||||
# scanf for Ruby
|
||||
#
|
||||
#--
|
||||
# $Release Version: 1.1.2 $
|
||||
# $Revision$
|
||||
# $Id$
|
||||
# $Author$
|
||||
#++
|
||||
#
|
||||
# == Description
|
||||
#
|
||||
# scanf is an implementation of the C function scanf(3), modified as necessary
|
||||
# for Ruby compatibility.
|
||||
#
|
||||
# The methods provided are String#scanf, IO#scanf, and
|
||||
# Kernel#scanf. Kernel#scanf is a wrapper around STDIN.scanf. IO#scanf
|
||||
# can be used on any IO stream, including file handles and sockets.
|
||||
# scanf can be called either with or without a block.
|
||||
#
|
||||
# Scanf scans an input string or stream according to a <b>format</b>, as
|
||||
# described below in Conversions, and returns an array of matches between
|
||||
# the format and the input. The format is defined in a string, and is
|
||||
# similar (though not identical) to the formats used in Kernel#printf and
|
||||
# Kernel#sprintf.
|
||||
#
|
||||
# The format may contain <b>conversion specifiers</b>, which tell scanf
|
||||
# what form (type) each particular matched substring should be converted
|
||||
# to (e.g., decimal integer, floating point number, literal string,
|
||||
# etc.) The matches and conversions take place from left to right, and
|
||||
# the conversions themselves are returned as an array.
|
||||
#
|
||||
# The format string may also contain characters other than those in the
|
||||
# conversion specifiers. Whitespace (blanks, tabs, or newlines) in the
|
||||
# format string matches any amount of whitespace, including none, in
|
||||
# the input. Everything else matches only itself.
|
||||
#
|
||||
# Scanning stops, and scanf returns, when any input character fails to
|
||||
# match the specifications in the format string, or when input is
|
||||
# exhausted, or when everything in the format string has been
|
||||
# matched. All matches found up to the stopping point are returned in
|
||||
# the return array (or yielded to the block, if a block was given).
|
||||
#
|
||||
#
|
||||
# == Basic usage
|
||||
#
|
||||
# require 'scanf'
|
||||
#
|
||||
# # String#scanf and IO#scanf take a single argument, the format string
|
||||
# array = a_string.scanf("%d%s")
|
||||
# array = an_io.scanf("%d%s")
|
||||
#
|
||||
# # Kernel#scanf reads from STDIN
|
||||
# array = scanf("%d%s")
|
||||
#
|
||||
# == Block usage
|
||||
#
|
||||
# When called with a block, scanf keeps scanning the input, cycling back
|
||||
# to the beginning of the format string, and yields a new array of
|
||||
# conversions to the block every time the format string is matched
|
||||
# (including partial matches, but not including complete failures). The
|
||||
# actual return value of scanf when called with a block is an array
|
||||
# containing the results of all the executions of the block.
|
||||
#
|
||||
# str = "123 abc 456 def 789 ghi"
|
||||
# str.scanf("%d%s") { |num,str| [ num * 2, str.upcase ] }
|
||||
# # => [[246, "ABC"], [912, "DEF"], [1578, "GHI"]]
|
||||
#
|
||||
# == Conversions
|
||||
#
|
||||
# The single argument to scanf is a format string, which generally
|
||||
# includes one or more conversion specifiers. Conversion specifiers
|
||||
# begin with the percent character ('%') and include information about
|
||||
# what scanf should next scan for (string, decimal number, single
|
||||
# character, etc.).
|
||||
#
|
||||
# There may be an optional maximum field width, expressed as a decimal
|
||||
# integer, between the % and the conversion. If no width is given, a
|
||||
# default of `infinity' is used (with the exception of the %c specifier;
|
||||
# see below). Otherwise, given a field width of <em>n</em> for a given
|
||||
# conversion, at most <em>n</em> characters are scanned in processing
|
||||
# that conversion. Before conversion begins, most conversions skip
|
||||
# whitespace in the input string; this whitespace is not counted
|
||||
# against the field width.
|
||||
#
|
||||
# The following conversions are available.
|
||||
#
|
||||
# [%]
|
||||
# Matches a literal `%'. That is, `%%' in the format string matches a
|
||||
# single input `%' character. No conversion is done, and the resulting
|
||||
# '%' is not included in the return array.
|
||||
#
|
||||
# [d]
|
||||
# Matches an optionally signed decimal integer.
|
||||
#
|
||||
# [u]
|
||||
# Same as d.
|
||||
#
|
||||
# [i]
|
||||
# Matches an optionally signed integer. The integer is read in base
|
||||
# 16 if it begins with `0x' or `0X', in base 8 if it begins with `0',
|
||||
# and in base 10 other- wise. Only characters that correspond to the
|
||||
# base are recognized.
|
||||
#
|
||||
# [o]
|
||||
# Matches an optionally signed octal integer.
|
||||
#
|
||||
# [x, X]
|
||||
# Matches an optionally signed hexadecimal integer,
|
||||
#
|
||||
# [a, e, f, g, A, E, F, G]
|
||||
# Matches an optionally signed floating-point number.
|
||||
#
|
||||
# [s]
|
||||
# Matches a sequence of non-white-space character. The input string stops at
|
||||
# whitespace or at the maximum field width, whichever occurs first.
|
||||
#
|
||||
# [c]
|
||||
# Matches a single character, or a sequence of <em>n</em> characters if a
|
||||
# field width of <em>n</em> is specified. The usual skip of leading white
|
||||
# space is suppressed. To skip whitespace first, use an explicit space in
|
||||
# the format.
|
||||
#
|
||||
# [[]
|
||||
# Matches a nonempty sequence of characters from the specified set
|
||||
# of accepted characters. The usual skip of leading whitespace is
|
||||
# suppressed. This bracketed sub-expression is interpreted exactly like a
|
||||
# character class in a Ruby regular expression. (In fact, it is placed as-is
|
||||
# in a regular expression.) The matching against the input string ends with
|
||||
# the appearance of a character not in (or, with a circumflex, in) the set,
|
||||
# or when the field width runs out, whichever comes first.
|
||||
#
|
||||
# === Assignment suppression
|
||||
#
|
||||
# To require that a particular match occur, but without including the result
|
||||
# in the return array, place the <b>assignment suppression flag</b>, which is
|
||||
# the star character ('*'), immediately after the leading '%' of a format
|
||||
# specifier (just before the field width, if any).
|
||||
#
|
||||
# == scanf for Ruby compared with scanf in C
|
||||
#
|
||||
# scanf for Ruby is based on the C function scanf(3), but with modifications,
|
||||
# dictated mainly by the underlying differences between the languages.
|
||||
#
|
||||
# === Unimplemented flags and specifiers
|
||||
#
|
||||
# * The only flag implemented in scanf for Ruby is '<tt>*</tt>' (ignore
|
||||
# upcoming conversion). Many of the flags available in C versions of
|
||||
# scanf(3) have to do with the type of upcoming pointer arguments, and are
|
||||
# meaningless in Ruby.
|
||||
#
|
||||
# * The <tt>n</tt> specifier (store number of characters consumed so far in
|
||||
# next pointer) is not implemented.
|
||||
#
|
||||
# * The <tt>p</tt> specifier (match a pointer value) is not implemented.
|
||||
#
|
||||
# === Altered specifiers
|
||||
#
|
||||
# [o, u, x, X]
|
||||
# In scanf for Ruby, all of these specifiers scan for an optionally signed
|
||||
# integer, rather than for an unsigned integer like their C counterparts.
|
||||
#
|
||||
# === Return values
|
||||
#
|
||||
# scanf for Ruby returns an array of successful conversions, whereas
|
||||
# scanf(3) returns the number of conversions successfully
|
||||
# completed. (See below for more details on scanf for Ruby's return
|
||||
# values.)
|
||||
#
|
||||
# == Return values
|
||||
#
|
||||
# Without a block, scanf returns an array containing all the conversions
|
||||
# it has found. If none are found, scanf will return an empty array. An
|
||||
# unsuccessful match is never ignored, but rather always signals the end
|
||||
# of the scanning operation. If the first unsuccessful match takes place
|
||||
# after one or more successful matches have already taken place, the
|
||||
# returned array will contain the results of those successful matches.
|
||||
#
|
||||
# With a block scanf returns a 'map'-like array of transformations from
|
||||
# the block -- that is, an array reflecting what the block did with each
|
||||
# yielded result from the iterative scanf operation. (See "Block
|
||||
# usage", above.)
|
||||
#
|
||||
# == Current limitations and bugs
|
||||
#
|
||||
# When using IO#scanf under Windows, make sure you open your files in
|
||||
# binary mode:
|
||||
#
|
||||
# File.open("filename", "rb")
|
||||
#
|
||||
# so that scanf can keep track of characters correctly.
|
||||
#
|
||||
# Support for character classes is reasonably complete (since it
|
||||
# essentially piggy-backs on Ruby's regular expression handling of
|
||||
# character classes), but users are advised that character class testing
|
||||
# has not been exhaustive, and that they should exercise some caution
|
||||
# in using any of the more complex and/or arcane character class
|
||||
# idioms.
|
||||
#
|
||||
# == License and copyright
|
||||
#
|
||||
# Copyright:: (c) 2002-2003 David Alan Black
|
||||
# License:: Distributed on the same licensing terms as Ruby itself
|
||||
#
|
||||
# == Warranty disclaimer
|
||||
#
|
||||
# This software is provided "as is" and without any express or implied
|
||||
# warranties, including, without limitation, the implied warranties of
|
||||
# merchantability and fitness for a particular purpose.
|
||||
#
|
||||
# == Credits and acknowledgements
|
||||
#
|
||||
# scanf was developed as the major activity of the Austin Ruby Codefest
|
||||
# (Austin, Texas, August 2002).
|
||||
#
|
||||
# Principal author:: David Alan Black (mailto:dblack@superlink.net)
|
||||
# Co-author:: Hal Fulton (mailto:hal9000@hypermetrics.com)
|
||||
# Project contributors:: Nolan Darilek, Jason Johnston
|
||||
#
|
||||
# Thanks to Hal Fulton for hosting the Codefest.
|
||||
#
|
||||
# Thanks to Matz for suggestions about the class design.
|
||||
#
|
||||
# Thanks to Gavin Sinclair for some feedback on the documentation.
|
||||
#
|
||||
# The text for parts of this document, especially the Description and
|
||||
# Conversions sections, above, were adapted from the Linux Programmer's
|
||||
# Manual manpage for scanf(3), dated 1995-11-01.
|
||||
#
|
||||
# == Bugs and bug reports
|
||||
#
|
||||
# scanf for Ruby is based on something of an amalgam of C scanf
|
||||
# implementations and documentation, rather than on a single canonical
|
||||
# description. Suggestions for features and behaviors which appear in
|
||||
# other scanfs, and would be meaningful in Ruby, are welcome, as are
|
||||
# reports of suspicious behaviors and/or bugs. (Please see "Credits and
|
||||
# acknowledgements", above, for email addresses.)
|
||||
|
||||
module Scanf
|
||||
# :stopdoc:
|
||||
|
||||
# ==Technical notes
|
||||
#
|
||||
# ===Rationale behind scanf for Ruby
|
||||
#
|
||||
# The impetus for a scanf implementation in Ruby comes chiefly from the fact
|
||||
# that existing pattern matching operations, such as Regexp#match and
|
||||
# String#scan, return all results as strings, which have to be converted to
|
||||
# integers or floats explicitly in cases where what's ultimately wanted are
|
||||
# integer or float values.
|
||||
#
|
||||
# ===Design of scanf for Ruby
|
||||
#
|
||||
# scanf for Ruby is essentially a <format string>-to-<regular
|
||||
# expression> converter.
|
||||
#
|
||||
# When scanf is called, a FormatString object is generated from the
|
||||
# format string ("%d%s...") argument. The FormatString object breaks the
|
||||
# format string down into atoms ("%d", "%5f", "blah", etc.), and from
|
||||
# each atom it creates a FormatSpecifier object, which it
|
||||
# saves.
|
||||
#
|
||||
# Each FormatSpecifier has a regular expression fragment and a "handler"
|
||||
# associated with it. For example, the regular expression fragment
|
||||
# associated with the format "%d" is "([-+]?\d+)", and the handler
|
||||
# associated with it is a wrapper around String#to_i. scanf itself calls
|
||||
# FormatString#match, passing in the input string. FormatString#match
|
||||
# iterates through its FormatSpecifiers; for each one, it matches the
|
||||
# corresponding regular expression fragment against the string. If
|
||||
# there's a match, it sends the matched string to the handler associated
|
||||
# with the FormatSpecifier.
|
||||
#
|
||||
# Thus, to follow up the "%d" example: if "123" occurs in the input
|
||||
# string when a FormatSpecifier consisting of "%d" is reached, the "123"
|
||||
# will be matched against "([-+]?\d+)", and the matched string will be
|
||||
# rendered into an integer by a call to to_i.
|
||||
#
|
||||
# The rendered match is then saved to an accumulator array, and the
|
||||
# input string is reduced to the post-match substring. Thus the string
|
||||
# is "eaten" from the left as the FormatSpecifiers are applied in
|
||||
# sequence. (This is done to a duplicate string; the original string is
|
||||
# not altered.)
|
||||
#
|
||||
# As soon as a regular expression fragment fails to match the string, or
|
||||
# when the FormatString object runs out of FormatSpecifiers, scanning
|
||||
# stops and results accumulated so far are returned in an array.
|
||||
|
||||
class FormatSpecifier
|
||||
|
||||
attr_reader :re_string, :matched_string, :conversion, :matched
|
||||
|
||||
private
|
||||
|
||||
def skip; /^\s*%\*/.match(@spec_string); end
|
||||
|
||||
def extract_float(s)
|
||||
return nil unless s &&! skip
|
||||
if /\A(?<sign>[-+]?)0[xX](?<frac>\.\h+|\h+(?:\.\h*)?)[pP](?<exp>[-+]?\d+)/ =~ s
|
||||
f1, f2 = frac.split('.')
|
||||
f = f1.hex
|
||||
if f2
|
||||
len = f2.length
|
||||
if len > 0
|
||||
f += f2.hex / (16.0 ** len)
|
||||
end
|
||||
end
|
||||
(sign == ?- ? -1 : 1) * Math.ldexp(f, exp.to_i)
|
||||
elsif /\A([-+]?\d+)\.([eE][-+]\d+)/ =~ s
|
||||
($1 << $2).to_f
|
||||
else
|
||||
s.to_f
|
||||
end
|
||||
end
|
||||
def extract_decimal(s); s.to_i if s &&! skip; end
|
||||
def extract_hex(s); s.hex if s &&! skip; end
|
||||
def extract_octal(s); s.oct if s &&! skip; end
|
||||
def extract_integer(s); Integer(s) if s &&! skip; end
|
||||
def extract_plain(s); s unless skip; end
|
||||
|
||||
def nil_proc(s); nil; end
|
||||
|
||||
public
|
||||
|
||||
def to_s
|
||||
@spec_string
|
||||
end
|
||||
|
||||
def count_space?
|
||||
/(?:\A|\S)%\*?\d*c|%\d*\[/.match(@spec_string)
|
||||
end
|
||||
|
||||
def initialize(str)
|
||||
@spec_string = str
|
||||
h = '[A-Fa-f0-9]'
|
||||
|
||||
@re_string, @handler =
|
||||
case @spec_string
|
||||
|
||||
# %[[:...:]]
|
||||
when /%\*?(\[\[:[a-z]+:\]\])/
|
||||
[ "(#{$1}+)", :extract_plain ]
|
||||
|
||||
# %5[[:...:]]
|
||||
when /%\*?(\d+)(\[\[:[a-z]+:\]\])/
|
||||
[ "(#{$2}{1,#{$1}})", :extract_plain ]
|
||||
|
||||
# %[...]
|
||||
when /%\*?\[([^\]]*)\]/
|
||||
yes = $1
|
||||
if /^\^/.match(yes) then no = yes[1..-1] else no = '^' + yes end
|
||||
[ "([#{yes}]+)(?=[#{no}]|\\z)", :extract_plain ]
|
||||
|
||||
# %5[...]
|
||||
when /%\*?(\d+)\[([^\]]*)\]/
|
||||
yes = $2
|
||||
w = $1
|
||||
[ "([#{yes}]{1,#{w}})", :extract_plain ]
|
||||
|
||||
# %i
|
||||
when /%\*?i/
|
||||
[ "([-+]?(?:(?:0[0-7]+)|(?:0[Xx]#{h}+)|(?:[1-9]\\d*)))", :extract_integer ]
|
||||
|
||||
# %5i
|
||||
when /%\*?(\d+)i/
|
||||
n = $1.to_i
|
||||
s = "("
|
||||
if n > 1 then s += "[1-9]\\d{1,#{n-1}}|" end
|
||||
if n > 1 then s += "0[0-7]{1,#{n-1}}|" end
|
||||
if n > 2 then s += "[-+]0[0-7]{1,#{n-2}}|" end
|
||||
if n > 2 then s += "[-+][1-9]\\d{1,#{n-2}}|" end
|
||||
if n > 2 then s += "0[Xx]#{h}{1,#{n-2}}|" end
|
||||
if n > 3 then s += "[-+]0[Xx]#{h}{1,#{n-3}}|" end
|
||||
s += "\\d"
|
||||
s += ")"
|
||||
[ s, :extract_integer ]
|
||||
|
||||
# %d, %u
|
||||
when /%\*?[du]/
|
||||
[ '([-+]?\d+)', :extract_decimal ]
|
||||
|
||||
# %5d, %5u
|
||||
when /%\*?(\d+)[du]/
|
||||
n = $1.to_i
|
||||
s = "("
|
||||
if n > 1 then s += "[-+]\\d{1,#{n-1}}|" end
|
||||
s += "\\d{1,#{$1}})"
|
||||
[ s, :extract_decimal ]
|
||||
|
||||
# %x
|
||||
when /%\*?[Xx]/
|
||||
[ "([-+]?(?:0[Xx])?#{h}+)", :extract_hex ]
|
||||
|
||||
# %5x
|
||||
when /%\*?(\d+)[Xx]/
|
||||
n = $1.to_i
|
||||
s = "("
|
||||
if n > 3 then s += "[-+]0[Xx]#{h}{1,#{n-3}}|" end
|
||||
if n > 2 then s += "0[Xx]#{h}{1,#{n-2}}|" end
|
||||
if n > 1 then s += "[-+]#{h}{1,#{n-1}}|" end
|
||||
s += "#{h}{1,#{n}}"
|
||||
s += ")"
|
||||
[ s, :extract_hex ]
|
||||
|
||||
# %o
|
||||
when /%\*?o/
|
||||
[ '([-+]?[0-7]+)', :extract_octal ]
|
||||
|
||||
# %5o
|
||||
when /%\*?(\d+)o/
|
||||
[ "([-+][0-7]{1,#{$1.to_i-1}}|[0-7]{1,#{$1}})", :extract_octal ]
|
||||
|
||||
# %f
|
||||
when /%\*?[aefgAEFG]/
|
||||
[ '([-+]?(?:0[xX](?:\.\h+|\h+(?:\.\h*)?)[pP][-+]?\d+|\d+(?![\d.])|\d*\.\d*(?:[eE][-+]?\d+)?))', :extract_float ]
|
||||
|
||||
# %5f
|
||||
when /%\*?(\d+)[aefgAEFG]/
|
||||
[ '(?=[-+]?(?:0[xX](?:\.\h+|\h+(?:\.\h*)?)[pP][-+]?\d+|\d+(?![\d.])|\d*\.\d*(?:[eE][-+]?\d+)?))' +
|
||||
"(\\S{1,#{$1}})", :extract_float ]
|
||||
|
||||
# %5s
|
||||
when /%\*?(\d+)s/
|
||||
[ "(\\S{1,#{$1}})", :extract_plain ]
|
||||
|
||||
# %s
|
||||
when /%\*?s/
|
||||
[ '(\S+)', :extract_plain ]
|
||||
|
||||
# %c
|
||||
when /\s%\*?c/
|
||||
[ "\\s*(.)", :extract_plain ]
|
||||
|
||||
# %c
|
||||
when /%\*?c/
|
||||
[ "(.)", :extract_plain ]
|
||||
|
||||
# %5c (whitespace issues are handled by the count_*_space? methods)
|
||||
when /%\*?(\d+)c/
|
||||
[ "(.{1,#{$1}})", :extract_plain ]
|
||||
|
||||
# %%
|
||||
when /%%/
|
||||
[ '(\s*%)', :nil_proc ]
|
||||
|
||||
# literal characters
|
||||
else
|
||||
[ "(#{Regexp.escape(@spec_string)})", :nil_proc ]
|
||||
end
|
||||
|
||||
@re_string = '\A' + @re_string
|
||||
end
|
||||
|
||||
def to_re
|
||||
Regexp.new(@re_string,Regexp::MULTILINE)
|
||||
end
|
||||
|
||||
def match(str)
|
||||
@matched = false
|
||||
s = str.dup
|
||||
s.sub!(/\A\s+/,'') unless count_space?
|
||||
res = to_re.match(s)
|
||||
if res
|
||||
@conversion = send(@handler, res[1])
|
||||
@matched_string = @conversion.to_s
|
||||
@matched = true
|
||||
end
|
||||
res
|
||||
end
|
||||
|
||||
def letter
|
||||
@spec_string[/%\*?\d*([a-z\[])/, 1]
|
||||
end
|
||||
|
||||
def width
|
||||
@spec_string[/%\*?(\d+)/, 1]&.to_i
|
||||
end
|
||||
|
||||
def mid_match?
|
||||
return false unless @matched
|
||||
cc_no_width = letter == '[' &&! width
|
||||
c_or_cc_width = (letter == 'c' || letter == '[') && width
|
||||
width_left = c_or_cc_width && (matched_string.size < width)
|
||||
|
||||
return width_left || cc_no_width
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
class FormatString
|
||||
|
||||
attr_reader :string_left, :last_spec_tried,
|
||||
:last_match_tried, :matched_count, :space
|
||||
|
||||
SPECIFIERS = 'diuXxofFeEgGscaA'
|
||||
REGEX = /
|
||||
# possible space, followed by...
|
||||
(?:\s*
|
||||
# percent sign, followed by...
|
||||
%
|
||||
# another percent sign, or...
|
||||
(?:%|
|
||||
# optional assignment suppression flag
|
||||
\*?
|
||||
# optional maximum field width
|
||||
\d*
|
||||
# named character class, ...
|
||||
(?:\[\[:\w+:\]\]|
|
||||
# traditional character class, or...
|
||||
\[[^\]]*\]|
|
||||
# specifier letter.
|
||||
[#{SPECIFIERS}])))|
|
||||
# or miscellaneous characters
|
||||
[^%\s]+/ix
|
||||
|
||||
def initialize(str)
|
||||
@specs = []
|
||||
@i = 1
|
||||
s = str.to_s
|
||||
return unless /\S/.match(s)
|
||||
@space = true if /\s\z/.match(s)
|
||||
@specs.replace s.scan(REGEX).map {|spec| FormatSpecifier.new(spec) }
|
||||
end
|
||||
|
||||
def to_s
|
||||
@specs.join('')
|
||||
end
|
||||
|
||||
def prune(n=matched_count)
|
||||
n.times { @specs.shift }
|
||||
end
|
||||
|
||||
def spec_count
|
||||
@specs.size
|
||||
end
|
||||
|
||||
def last_spec
|
||||
@i == spec_count - 1
|
||||
end
|
||||
|
||||
def match(str)
|
||||
accum = []
|
||||
@string_left = str
|
||||
@matched_count = 0
|
||||
|
||||
@specs.each_with_index do |spec,i|
|
||||
@i=i
|
||||
@last_spec_tried = spec
|
||||
@last_match_tried = spec.match(@string_left)
|
||||
break unless @last_match_tried
|
||||
@matched_count += 1
|
||||
|
||||
accum << spec.conversion
|
||||
|
||||
@string_left = @last_match_tried.post_match
|
||||
break if @string_left.empty?
|
||||
end
|
||||
return accum.compact
|
||||
end
|
||||
end
|
||||
# :startdoc:
|
||||
end
|
||||
|
||||
class IO
|
||||
|
||||
#:stopdoc:
|
||||
# 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]
|
||||
#
|
||||
#:startdoc:
|
||||
|
||||
# Scans the current string until the match is exhausted,
|
||||
# yielding each match as it is encountered in the string.
|
||||
# A block is not necessary though, as the results will simply
|
||||
# be aggregated into the final array.
|
||||
#
|
||||
# "123 456".block_scanf("%d")
|
||||
# # => [123, 456]
|
||||
#
|
||||
# If a block is given, the value from that is returned from
|
||||
# the yield is added to an output array.
|
||||
#
|
||||
# "123 456".block_scanf("%d") do |digit,| # the ',' unpacks the Array
|
||||
# digit + 100
|
||||
# end
|
||||
# # => [223, 556]
|
||||
#
|
||||
# See Scanf for details on creating a format string.
|
||||
#
|
||||
# You will need to require 'scanf' to use IO#scanf.
|
||||
def scanf(str,&b) #:yield: current_match
|
||||
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
|
||||
|
||||
begin
|
||||
seek(start_position + matched_so_far, IO::SEEK_SET)
|
||||
rescue Errno::ESPIPE, Errno::EINVAL
|
||||
end
|
||||
|
||||
soak_up_spaces if fstr.last_spec && fstr.space
|
||||
|
||||
return final_result
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def soak_up_spaces
|
||||
c = getc
|
||||
ungetc(c) if c
|
||||
until eof ||! c || /\S/.match(c.chr)
|
||||
c = getc
|
||||
end
|
||||
ungetc(c) if (c && /\S/.match(c.chr))
|
||||
end
|
||||
|
||||
def block_scanf(str)
|
||||
final = []
|
||||
# Sub-ideal, since another FS gets created in scanf.
|
||||
# But used here to determine the number of specifiers.
|
||||
fstr = Scanf::FormatString.new(str)
|
||||
last_spec = fstr.last_spec
|
||||
begin
|
||||
current = scanf(str)
|
||||
break if current.empty?
|
||||
final.push(yield(current))
|
||||
end until eof || fstr.last_spec_tried == last_spec
|
||||
return final
|
||||
end
|
||||
end
|
||||
|
||||
class String
|
||||
|
||||
# :section: scanf
|
||||
#
|
||||
# You will need to require 'scanf' to use these methods
|
||||
|
||||
# Scans the current string. If a block is given, it
|
||||
# functions exactly like block_scanf.
|
||||
#
|
||||
# arr = "123 456".scanf("%d%d")
|
||||
# # => [123, 456]
|
||||
#
|
||||
# require 'pp'
|
||||
#
|
||||
# "this 123 read that 456 other".scanf("%s%d%s") {|m| pp m}
|
||||
#
|
||||
# # ["this", 123, "read"]
|
||||
# # ["that", 456, "other"]
|
||||
# # => [["this", 123, "read"], ["that", 456, "other"]]
|
||||
#
|
||||
# See Scanf for details on creating a format string.
|
||||
#
|
||||
# You will need to require 'scanf' to use String#scanf
|
||||
def scanf(fstr,&b) #:yield: current_match
|
||||
if b
|
||||
block_scanf(fstr,&b)
|
||||
else
|
||||
fs =
|
||||
if fstr.is_a? Scanf::FormatString
|
||||
fstr
|
||||
else
|
||||
Scanf::FormatString.new(fstr)
|
||||
end
|
||||
fs.match(self)
|
||||
end
|
||||
end
|
||||
|
||||
# Scans the current string until the match is exhausted
|
||||
# yielding each match as it is encountered in the string.
|
||||
# A block is not necessary as the results will simply
|
||||
# be aggregated into the final array.
|
||||
#
|
||||
# "123 456".block_scanf("%d")
|
||||
# # => [123, 456]
|
||||
#
|
||||
# If a block is given, the value from that is returned from
|
||||
# the yield is added to an output array.
|
||||
#
|
||||
# "123 456".block_scanf("%d") do |digit,| # the ',' unpacks the Array
|
||||
# digit + 100
|
||||
# end
|
||||
# # => [223, 556]
|
||||
#
|
||||
# See Scanf for details on creating a format string.
|
||||
#
|
||||
# You will need to require 'scanf' to use String#block_scanf
|
||||
def block_scanf(fstr) #:yield: current_match
|
||||
fs = Scanf::FormatString.new(fstr)
|
||||
str = self.dup
|
||||
final = []
|
||||
begin
|
||||
current = str.scanf(fs)
|
||||
final.push(yield(current)) unless current.empty?
|
||||
str = fs.string_left
|
||||
end until current.empty? || str.empty?
|
||||
return final
|
||||
end
|
||||
end
|
||||
|
||||
module Kernel
|
||||
private
|
||||
# Scans STDIN for data matching +format+. See IO#scanf for details.
|
||||
#
|
||||
# See Scanf for details on creating a format string.
|
||||
#
|
||||
# You will need to require 'scanf' to use Kernel#scanf.
|
||||
def scanf(format, &b) #:doc:
|
||||
STDIN.scanf(format ,&b)
|
||||
end
|
||||
end
|
|
@ -1,7 +0,0 @@
|
|||
require_relative '../../../spec_helper'
|
||||
require_relative 'shared/block_scanf'
|
||||
require 'scanf'
|
||||
|
||||
describe "IO#block_scanf" do
|
||||
it_behaves_like :scanf_io_block_scanf, :block_scanf
|
||||
end
|
|
@ -1,4 +0,0 @@
|
|||
Beethoven 1770
|
||||
Bach 1685
|
||||
Handel 1685
|
||||
|
|
@ -1 +0,0 @@
|
|||
hello world
|
|
@ -1,35 +0,0 @@
|
|||
require_relative '../../../spec_helper'
|
||||
require_relative 'shared/block_scanf'
|
||||
require 'scanf'
|
||||
|
||||
describe "IO#scanf" do
|
||||
before :each do
|
||||
@hw = File.open(File.dirname(__FILE__) + '/fixtures/helloworld.txt', 'r')
|
||||
@data = File.open(File.dirname(__FILE__) + '/fixtures/date.txt', 'r')
|
||||
end
|
||||
|
||||
after :each do
|
||||
@hw.close unless @hw.closed?
|
||||
@data.close unless @data.closed?
|
||||
end
|
||||
|
||||
it "returns an array containing the input converted in the specified type" do
|
||||
@hw.scanf("%s%s").should == ["hello", "world"]
|
||||
@data.scanf("%s%d").should == ["Beethoven", 1770]
|
||||
end
|
||||
|
||||
it "returns an array containing the input converted in the specified type with given maximum field width" do
|
||||
@hw.scanf("%2s").should == ["he"]
|
||||
@data.scanf("%2c").should == ["Be"]
|
||||
end
|
||||
|
||||
it "returns an empty array when a wrong specifier is passed" do
|
||||
@hw.scanf("%a").should == []
|
||||
@hw.scanf("%1").should == []
|
||||
@data.scanf("abc").should == []
|
||||
end
|
||||
end
|
||||
|
||||
describe "IO#scanf with block" do
|
||||
it_behaves_like :scanf_io_block_scanf, :scanf
|
||||
end
|
|
@ -1,28 +0,0 @@
|
|||
require 'scanf'
|
||||
|
||||
describe :scanf_io_block_scanf, shared: true do
|
||||
before :each do
|
||||
@data= File.open(File.dirname(__FILE__) + '/../fixtures/date.txt', 'r')
|
||||
end
|
||||
|
||||
after :each do
|
||||
@data.close unless @data.closed?
|
||||
end
|
||||
|
||||
it "passes each match to the block as an array" do
|
||||
res = @data.send(@method, "%s%d") { |name, year| "#{name} was born in #{year}." }
|
||||
res.should == ["Beethoven was born in 1770.", "Bach was born in 1685.", "Handel was born in 1685."]
|
||||
end
|
||||
|
||||
it "keeps scanning the input and cycling back to the beginning of the input string" do
|
||||
a = []
|
||||
@data.send(@method, "%s"){|w| a << w}
|
||||
a.should == [["Beethoven"], ["1770"], ["Bach"], ["1685"], ["Handel"], ["1685"]]
|
||||
end
|
||||
|
||||
it "returns an empty array when a wrong specifier is passed" do
|
||||
a = []
|
||||
@data.send(@method, "%z"){|w| a << w}
|
||||
a.empty?.should be_true
|
||||
end
|
||||
end
|
|
@ -1,7 +0,0 @@
|
|||
require_relative '../../../spec_helper'
|
||||
require_relative 'shared/block_scanf'
|
||||
require 'scanf'
|
||||
|
||||
describe "String#block_scanf" do
|
||||
it_behaves_like :scanf_string_block_scanf, :block_scanf
|
||||
end
|
|
@ -1,53 +0,0 @@
|
|||
require_relative '../../../spec_helper'
|
||||
require_relative 'shared/block_scanf'
|
||||
require 'scanf'
|
||||
|
||||
describe "String#scanf" do
|
||||
it "returns an array containing the input converted in the specified type" do
|
||||
"hello world".scanf("%s").should == ["hello"]
|
||||
"hello world".scanf("%s%d").should == ["hello"]
|
||||
"hello world".scanf("%s%c").should == ["hello", " "]
|
||||
"hello world".scanf("%c%s").should == ["h", "ello"]
|
||||
"hello world".scanf("%s%s").should == ["hello", "world"]
|
||||
"hello world".scanf("%c").should == ["h"]
|
||||
"123".scanf("%s").should == ["123"]
|
||||
"123".scanf("%c").should == ["1"]
|
||||
"123".scanf("%d").should == [123]
|
||||
"123".scanf("%u").should == [123]
|
||||
"123".scanf("%o").should == [83]
|
||||
"123".scanf("%x").should == [291]
|
||||
"123".scanf("%i").should == [123]
|
||||
"0123".scanf("%i").should == [83]
|
||||
"123".scanf("%f").should == [123.0]
|
||||
"0X123".scanf("%i").should == [291]
|
||||
"0x123".scanf("%i").should == [291]
|
||||
end
|
||||
|
||||
it "returns an array containing the input converted in the specified type with given maximum field width" do
|
||||
"hello world".scanf("%2s").should == ["he"]
|
||||
"hello world".scanf("%2c").should == ["he"]
|
||||
"123".scanf("%2s").should == ["12"]
|
||||
"123".scanf("%2c").should == ["12"]
|
||||
"123".scanf("%2d").should == [12]
|
||||
"123".scanf("%2u").should == [12]
|
||||
"123".scanf("%2o").should == [10]
|
||||
"123".scanf("%2x").should == [18]
|
||||
"123".scanf("%2i").should == [12]
|
||||
"0123".scanf("%2i").should == [1]
|
||||
"123".scanf("%2f").should == [12.0]
|
||||
"0X123".scanf("%2i").should == [0]
|
||||
"0X123".scanf("%3i").should == [1]
|
||||
"0X123".scanf("%4i").should == [18]
|
||||
end
|
||||
|
||||
it "returns an empty array when a wrong specifier is passed" do
|
||||
"hello world".scanf("%a").should == []
|
||||
"123".scanf("%1").should == []
|
||||
"123".scanf("abc").should == []
|
||||
"123".scanf(:d).should == []
|
||||
end
|
||||
end
|
||||
|
||||
describe "String#scanf with block" do
|
||||
it_behaves_like :scanf_string_block_scanf, :scanf
|
||||
end
|
|
@ -1,25 +0,0 @@
|
|||
require 'scanf'
|
||||
|
||||
describe :scanf_string_block_scanf, shared: true do
|
||||
it "passes each match to the block as an array" do
|
||||
a = []
|
||||
"hello world".send(@method, "%s%s"){|w| a << w}
|
||||
a.should == [["hello", "world"]]
|
||||
end
|
||||
|
||||
it "keeps scanning the input and cycling back to the beginning of the input string" do
|
||||
a = []
|
||||
"hello world".send(@method, "%s"){|w| a << w}
|
||||
a.should == [["hello"], ["world"]]
|
||||
|
||||
string = "123 abc 456 def 789 ghi"
|
||||
s = string.send(@method, "%d%s"){|num,str| [num * 2, str.upcase]}
|
||||
s.should == [[246, "ABC"], [912, "DEF"], [1578, "GHI"]]
|
||||
end
|
||||
|
||||
it "returns an empty array when a wrong specifier is passed" do
|
||||
a = []
|
||||
"hello world".send(@method, "%z"){|w| a << w}
|
||||
a.empty?.should be_true
|
||||
end
|
||||
end
|
|
@ -1,6 +0,0 @@
|
|||
this is 33 a fun
|
||||
little input file
|
||||
|
||||
with
|
||||
|
||||
characters
|
|
@ -1,305 +0,0 @@
|
|||
# frozen_string_literal: false
|
||||
# $Id$
|
||||
#
|
||||
# scanf for Ruby
|
||||
#
|
||||
# Unit tests
|
||||
#
|
||||
|
||||
require 'scanf.rb'
|
||||
require 'test/unit'
|
||||
require 'tempfile'
|
||||
|
||||
# Comment out either of these lines to skip those tests.
|
||||
|
||||
class TestStringScanf < Test::Unit::TestCase;end
|
||||
class TestIOScanf < Test::Unit::TestCase;end
|
||||
|
||||
module ScanfTests
|
||||
|
||||
def tests
|
||||
[
|
||||
|
||||
# Scratchpad
|
||||
[ "%2[a]", "nbc", []],
|
||||
[ "%*d %*3d %*s", "123 +456 abc", [] ],
|
||||
[ "%d%c", "123 x", [ 123, " " ] ],
|
||||
[ "%d%c", "123x", [ 123, "x" ] ],
|
||||
[ "%d %c", "123x", [ 123, "x" ] ],
|
||||
[ "%d %c", "123 x", [ 123, "x" ] ],
|
||||
|
||||
# Testing failures
|
||||
[ "%x", "x", [] ],
|
||||
[ "%2x", "x", [] ],
|
||||
[ "%i", "x", [] ],
|
||||
# ]; end; def nothing; [
|
||||
[ "%2i", "x", [] ],
|
||||
[ "%2o", "x", [] ],
|
||||
[ "%d", "x", [] ],
|
||||
[ "%2d", "x", [] ],
|
||||
[ "%3d", "+x3", [] ],
|
||||
[ "%d%[abc]", "eabc", [] ],
|
||||
[ "%d\n%[abc]", "\neabc", [] ],
|
||||
[ "%d%[^abc]", "ghiabc", [ ] ],
|
||||
[ "%d%[abc]", "abc", [] ],
|
||||
[ "%d%s", "", [] ],
|
||||
[ "%d%s", "blah 123 string", [] ],
|
||||
[ "%[\n]", "abc\n", [] ],
|
||||
[ "%f", "x", [] ],
|
||||
[ "%f", "z", [] ],
|
||||
[ "%f", "z3.2534", [] ],
|
||||
[ "", "", [] ],
|
||||
[ "", "abc 123", [] ],
|
||||
[ '%[^\\w]%c', "a...1", [] ],
|
||||
|
||||
# Testing 'x'
|
||||
[ "%3x", "0xz", [0] ],
|
||||
|
||||
# Testing 'i'
|
||||
[ "%3i", "097", [0] ],
|
||||
[ "%3i", "0xz", [0] ],
|
||||
[ "%1i", "3", [ 3 ] ],
|
||||
[ "%2i", "07", [ 7 ] ],
|
||||
[ "%2i", "0a", [ 0 ] ],
|
||||
|
||||
# Testing 'c'
|
||||
[ "%3c", "abc", [ "abc" ] ],
|
||||
[ "%3c", "a\nb", [ "a\nb" ] ],
|
||||
[ "%3c", "a\nbcd", [ "a\nb" ] ],
|
||||
[ "%c\n\n", "x\n\n", [ "x" ] ],
|
||||
[ "%c", "\n", [ "\n" ] ],
|
||||
[ "%c", "x\n", [ "x" ] ],
|
||||
[ "%2c", " 123", [" 1"] ],
|
||||
[ " %c", " x", ["x"] ],
|
||||
[ "%c", " x", [" "] ],
|
||||
[ "%c", "123", ["1"] ],
|
||||
[ "%2c", "123", ["12"] ],
|
||||
[ "%5c", "a\nb\n\n", [ "a\nb\n\n" ] ],
|
||||
[ "%6c", "a\nb\n\nx", [ "a\nb\n\nx" ] ],
|
||||
[ "%5c", "ab\ncd", [ "ab\ncd" ] ],
|
||||
|
||||
# Testing 'o'
|
||||
[ "%3o", "0xz", [0] ],
|
||||
|
||||
# Testing 'd'
|
||||
[ "%d", "\n123", [ 123 ] ],
|
||||
[ "%d", "\n\n123", [ 123 ] ],
|
||||
[ "%1d", "2", [2] ],
|
||||
|
||||
# Mixed tests
|
||||
# Includes:
|
||||
# whitespace/newline
|
||||
# mixed integer bases
|
||||
# various mixed specifiers
|
||||
|
||||
[ "%[^\\w]%c", "...1", [ "...", "1"] ],
|
||||
[ "%[abc\n]%d", "a\n\nb\n\nc 123", [ "a\n\nb\n\nc", 123 ] ],
|
||||
[ "%[abc\n]%d", "a\n\nb\n\nc \t 123", [ "a\n\nb\n\nc", 123 ] ],
|
||||
[ "%[abc\t]%d", "a\t\tb\t\tc 123", [ "a\t\tb\t\tc", 123 ] ],
|
||||
[ "%d%3[abc\n]", "123a\nbeaab", [ 123, "a\nb" ] ],
|
||||
[ "%d%20c", "42 is the key", [ 42, " is the key" ] ],
|
||||
[ "%d %20c", "42 is the key", [ 42, "is the key" ] ],
|
||||
[ "%d%3[^abc\n]%d", "123de\nf123", [ 123, "de" ] ],
|
||||
[ "%d %4c", "3abc", [ 3, "abc" ] ],
|
||||
[ "%f%d\n%[abc]", "1\neabc", [1.0] ],
|
||||
[ "%d%3[abc]", "123aaab", [ 123, "aaa" ] ],
|
||||
[ "%d%3[abc]", "123 aaab", [ 123 ] ],
|
||||
[ "%d%3[abc]", "123aeaab", [ 123, "a" ] ],
|
||||
[ "%d%[^abc]", "123defabc", [123, "def" ] ],
|
||||
[ "%d%3[^abc]", "123defdef", [ 123, "def" ] ],
|
||||
[ "%d%3[^abc] ", "123defdef ", [ 123, "def" ] ],
|
||||
[ "%d%3[^abc]ghi", "123defghi", [ 123, "def" ] ],
|
||||
[ "%d%3[^abc]", "123adefdef", [ 123 ] ],
|
||||
[ "%d%3[^abc]", "123deafdef", [ 123, "de" ] ],
|
||||
[ "%d%3[^abc\n]", "123de\nf", [ 123, "de" ] ],
|
||||
[ "%s%c%c%s", "abc\n\ndef", ["abc", "\n","\n", "def" ] ],
|
||||
[ "%c%d", "\n\n123", [ "\n",123 ] ],
|
||||
[ "%s%c%d", "abc\n123", [ "abc", "\n", 123 ] ],
|
||||
[ "%s%c%d", "abc\n\n123", [ "abc", "\n", 123 ] ],
|
||||
[ "%c%d", "\t\n123", [ "\t",123 ] ],
|
||||
[ "%s%c%d", "abc\t\n123", [ "abc", "\t", 123 ] ],
|
||||
[ "%3c%d", "abc123", [ "abc", 123 ] ],
|
||||
[ "%3c\n%d", "abc123", [ "abc", 123 ] ],
|
||||
[ "%3c\n%d", "abc 123", [ "abc", 123 ] ],
|
||||
[ "%3c %d", "abc123", [ "abc", 123 ] ],
|
||||
[ "%3c\t%d", "abc \n 123", [ "abc", 123 ] ],
|
||||
[ "%3c\t%d", "abc \n 123 ", [ "abc", 123 ] ],
|
||||
[ "%3c%d", "a\nb123", [ "a\nb", 123 ] ],
|
||||
[ "%f%3c", "1.2x\ny", [ 1.2, "x\ny"] ],
|
||||
[ "%d\n%d\n%d", "123 456 789", [ 123,456,789 ] ],
|
||||
[ "%d\n%i%2d%x\n%d", "123 0718932", [ 123, 071, 89, 0x32] ],
|
||||
[ "%c\n%c", "x y", [ "x", "y" ] ],
|
||||
[ "%c\t%c", "x y", [ "x", "y" ] ],
|
||||
[ "%s\n%s", "x y", [ "x", "y" ] ],
|
||||
[ "%s%s\n", "x y", [ "x", "y" ] ],
|
||||
[ "%c\n\n%c", "x\n\ny", [ "x", "y" ] ],
|
||||
[ "%s%d%d", "abc\n123\n456", [ "abc", 123, 456 ] ],
|
||||
[ "%3s%c%3c%d", "1.2x\n\ny123", [ "1.2", "x", "\n\ny", 123 ] ],
|
||||
[ "%c\n%c", "x\n\ny", [ "x", "y" ] ],
|
||||
[ "%c %c", "x\n\ny", [ "x", "y" ] ],
|
||||
[ "%s\n\n%c", "x\n\ny", [ "x", "y" ] ],
|
||||
[ "%s\n\n%s", "x\n\ny", [ "x", "y" ] ],
|
||||
[ "%d\n\n%d", "23\n\n45", [ 23, 45 ] ],
|
||||
[ "%d\n%d", "23\n\n45", [ 23, 45 ] ],
|
||||
[ "%c\n\n%c", "x y", [ "x", "y" ] ],
|
||||
[ "%c%c", "x\n\ny", [ "x", "\n" ] ],
|
||||
[ "%c%c", "x\n", [ "x", "\n" ] ],
|
||||
[ "%d%c%c%d", "345 678", [ 345, " ", " ", 678] ],
|
||||
[ "%d %c%s", "123 x hello", [123, "x", "hello"] ],
|
||||
[ "%d%2c", "654 123", [654," 1"] ],
|
||||
[ "%5c%s", "a\nb\n\nxyz", [ "a\nb\n\n","xyz" ] ],
|
||||
[ "%s%[ xyz]%d", "hello x 32", ["hello", " x ", 32] ],
|
||||
[ "%5s%8[a-z]%d", "helloblahblah 32", ["hello", "blahblah", 32] ],
|
||||
[ '%s%[abcde\\s]%d', "hello badea 32", ["hello", " badea ", 32] ],
|
||||
[ '%d%[\\s]%c', "123 \n\t X", [ 123," \n\t ", "X"] ],
|
||||
[ "%4s%2c%c", "1.2x\n\ny", [ "1.2x", "\n\n","y"] ],
|
||||
[ "%f%c %3c%d", "1.2x\n\ny123", [ 1.2, "x", "y12", 3 ] ],
|
||||
[ "%s%5c", "abc ab\ncd", [ "abc", " ab\nc" ] ],
|
||||
[ "%5c%f", "ab\ncd1.2", [ "ab\ncd",1.2 ] ],
|
||||
[ "%5c%c", "ab\ncd1", [ "ab\ncd","1" ] ],
|
||||
[ "%f%c%2c%d", "1.2x\ny123", [ 1.2, "x", "\ny", 123 ] ],
|
||||
[ "%f%c%3c", "1.2x\ny123", [ 1.2, "x", "\ny1"] ],
|
||||
[ "%s\n%s", "blah\n\nand\nmore stuff", [ "blah", "and" ] ],
|
||||
[ "%o%d%x", "21912a3", [ "21".oct, 912, "a3".hex ] ],
|
||||
[ "%3o%4d%3x", "21912a3", [ "21".oct, 912, "a3".hex ] ],
|
||||
[ "%3o%4d%5x", "2191240xa3", [ "21".oct, 9124, "a3".hex ] ],
|
||||
[ "%3d%3x", "12abc", [12, "abc".hex] ],
|
||||
[ "%s%i%d", "hello +0xdef 123", [ "hello", "def".hex, 123] ],
|
||||
[ "%s%i%d", "hello -0xdef 123", [ "hello", -"def".hex, 123] ],
|
||||
[ "%s%i%i%i%i", "hello 012 -012 100 1", [ "hello", 10, -10, 100, 1 ] ],
|
||||
[ "%s%i%i%i%i", "hello 012 0x12 100 1", [ "hello", 10, 18, 100, 1 ] ],
|
||||
[ "%s%5i%3i%4i", "hello 0x123 123 0123", [ "hello", "0x123".hex, 123,"0123".oct] ],
|
||||
[ "%s%3i%4i", "hello 1230123", [ "hello", 123,"0123".oct] ],
|
||||
[ "%s%3i", "hello 1230", [ "hello", 123] ],
|
||||
[ "%s%5x%d", "hello 0xdef 123", [ "hello", "def".hex, 123] ],
|
||||
[ "%s%6x%d", "hello +0xdef 123", [ "hello", "def".hex, 123] ],
|
||||
[ "%s%6x%d", "hello -0xdef 123", [ "hello", -"def".hex, 123] ],
|
||||
[ "%s%4x%d", "hello -def 123", [ "hello", -"def".hex, 123] ],
|
||||
[ "%s%3x%d", "hello def 123", [ "hello", "def".hex, 123] ],
|
||||
[ "%s%x%d", "hello -def 123", [ "hello", -"def".hex, 123] ],
|
||||
[ "%s%x%d", "hello -0xdef 123", [ "hello", -"def".hex, 123] ],
|
||||
[ "%s%x%d", "hello 0xdef 123", [ "hello", "def".hex, 123] ],
|
||||
[ "%s%d%x%s", "hello 123 abc def", [ "hello", 123, "abc".hex, "def"] ],
|
||||
[ "%s%d%o%d", "hello 012 012 100", [ "hello", 12, 10, 100 ] ],
|
||||
[ "%s%d%o%d", "hello 012 -012 100", [ "hello", 12, -10, 100 ] ],
|
||||
[ "%s%o%x%d", "hello 012 0x12 100", [ "hello", 10, 18, 100 ] ],
|
||||
[ "%s%d%o%d", "hello 012 +01288", [ "hello", 12, 10, 88 ] ],
|
||||
[ "%f %d %s", "12.3e23 45 string", ["12.3e23".to_f, 45, "string"] ],
|
||||
[ "%f %d %s", "12.3e+23 45 string", ["12.3e23".to_f, 45, "string"] ],
|
||||
[ "%f %d %s", "12.3e-23 45 string", ["12.3e-23".to_f, 45, "string"] ],
|
||||
[ "%f %d %s", "-12.3e-23 45 string", ["-12.3e-23".to_f, 45, "string"] ],
|
||||
[ "%f %d %s", "12.e23 45 string", ["12.e23".to_f, 45, "string"] ],
|
||||
[ "%5f %d %s", "1.2e23 string", ["1.2e2".to_f, 3, "string"] ],
|
||||
[ "%5f%d %s", "1.2e23 string", ["1.2e2".to_f, 3, "string"] ],
|
||||
[ "%5f%d %d %s", "1.2e23 45 string", ["1.2e2".to_f, 3, 45, "string"] ],
|
||||
[ "%6f %d %d %s", "+1.2e23 45 string", ["1.2e2".to_f, 3, 45, "string"] ],
|
||||
[ "%d %d", "123 \n 345", [123, 345] ],
|
||||
[ "%d %*d", "123 \n 345", [123] ],
|
||||
[ "%d %3d789", "123 +45789", [123, 45] ],
|
||||
[ "%d %3d%d", "123 +456789", [123, 45, 6789] ],
|
||||
[ "%d %3dabc", "123 456abc", [123, 456] ],
|
||||
[ "%d %s", "123abc", [123, "abc"] ],
|
||||
[ "%d%s %s", "123 abc def", [123, "abc", "def"] ],
|
||||
[ "%s%s", "abc123 def", ["abc123", "def"] ],
|
||||
[ "%s%s %s", "123 abc def", ["123", "abc", "def"] ],
|
||||
[ "%s%%%s", "abc % def", ["abc", "def"] ],
|
||||
[ "%d %3d %s", "+123 456abc", [123, 456, "abc"] ],
|
||||
[ "%d %3d %s", "123 456abc", [123, 456, "abc"] ],
|
||||
[ "%d %3d %s", "123 +456 abc", [123, 45, "6"] ],
|
||||
[ "%d %3d %s", "-123-456abc", [-123, -45, "6abc"] ],
|
||||
[ "%dabc%d", "123abc345", [123, 345] ],
|
||||
[ "%d%5s%d", "123 abcde12", [123, "abcde", 12] ],
|
||||
[ "%5d%5s%5d", "12345abcde67890", [12345, "abcde", 67890] ],
|
||||
[ "%5d%*5s%5d", "12345abcde67890", [12345, 67890] ],
|
||||
[ " 12345%5s%5d", "12345abcde67890", [ "abcde", 67890] ],
|
||||
[ "%5dabcde%5d", "12345abcde67890", [ 12345, 67890] ],
|
||||
[ "%s%%%*s", "abc % def", ["abc"] ],
|
||||
[ "%*6s %d", "string 123", [123] ],
|
||||
[ "%d %*3d %s", "-123-456abc", [-123, "6abc"] ],
|
||||
[ "%d%s", "123", [123] ],
|
||||
[ "%s%d", "abc", ["abc"] ],
|
||||
[ "%f%x", "3.2e45x", ["3.2e45x".to_f] ],
|
||||
[ "%*5f%d %d %s", "1.2e23 45 string", [3, 45, "string"] ],
|
||||
[ "%5f%*d %d %s", "1.2e23 45 string", ["1.2e2".to_f, 45, "string"] ],
|
||||
[ "%*5f%*d %*d %s", "1.2e23 45 string", ["string"] ],
|
||||
[ "%f %*d %s", "12.e23 45 string", ["12.e23".to_f, "string"] ],
|
||||
[ "%s %f %s %d %x%c%c%c%c",
|
||||
"float: 1.2e23 dec/hex: 135a23 abc",
|
||||
["float:", "1.2e23".to_f, "dec/hex:", 135, "a23".hex, " ", "a", "b", "c" ] ],
|
||||
|
||||
# Testing 's'
|
||||
[ "%s\n", "blah\n\n\n", [ "blah" ] ],
|
||||
|
||||
# Testing '['
|
||||
[ "%[a\nb]", "a\nb", [ "a\nb" ] ],
|
||||
[ "%[abc]", "acb", [ "acb" ] ],
|
||||
[ "%[abc\n]", "a\nb", [ "a\nb" ] ],
|
||||
[ "%[^abc]", "defabc", [ "def" ] ],
|
||||
[ "%[-abc]", "abc-cba", [ "abc-cba" ] ],
|
||||
[ "%[\n]", "\n", [ "\n" ] ],
|
||||
[ "%[\n]", "\nabc", [ "\n" ] ],
|
||||
[ "%[\n\t]", "\t\n", [ "\t\n" ] ],
|
||||
[ "%[a-f]", "abczef", [ "abc" ] ],
|
||||
[ "%d%3[[:lower:]] %f", "123ade1.2", [ 123,"ade",1.2 ] ],
|
||||
[ "%d%3[[:lower:]] %f", "123ad1.2", [ 123,"ad",1.2 ] ],
|
||||
[ "%d%3[[:lower:]] %f", "123 ad1.2", [ 123 ] ],
|
||||
[ "%d%[[:lower:]]", "123abcdef1.2", [ 123, "abcdef" ] ],
|
||||
[ "%[[:lower:]]%d", "abcdef123", [ "abcdef", 123 ] ],
|
||||
[ "%[[:digit:]]%[[:alpha:]]", "123abcdef", [ "123", "abcdef" ] ],
|
||||
[ "%[[:digit:]]%d", "123 123", [ "123", 123 ] ],
|
||||
[ "%[[:upper:]]", "ABCdefGHI", [ "ABC" ] ],
|
||||
|
||||
# Testing 'f'
|
||||
[ "%2f", "x", [] ],
|
||||
[ "%F", "1.23e45", [1.23e+45] ],
|
||||
[ "%e", "3.25ee", [3.25] ],
|
||||
[ "%E", "3..25", [3.0] ],
|
||||
[ "%g", "+3.25", [3.25] ],
|
||||
[ "%G", "+3.25e2", [325.0] ],
|
||||
[ "%f", "3.z", [3.0] ],
|
||||
[ "%a", "0X1P+10", [1024.0] ],
|
||||
[ "%a", "0X1P10", [1024.0] ],
|
||||
[ "%A", "0x1.deadbeefp+99", [1.1851510441583988e+30] ],
|
||||
|
||||
# Testing embedded matches including literal '[' behavior
|
||||
[",%d,%f", ",10,1.1", [10,1.1] ],
|
||||
[" ,%d,%f", " ,10,1.1", [10,1.1] ],
|
||||
["[%d,%f", "[10,1.1", [10,1.1] ],
|
||||
[" [%d,%f", " [10,1.1", [10,1.1] ],
|
||||
|
||||
]
|
||||
end
|
||||
|
||||
def each_test
|
||||
self.tests.each do |test|
|
||||
format, string, = test
|
||||
yield test, "#{string.dump}(#{format.dump})"
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
class TestStringScanf
|
||||
include Scanf
|
||||
extend ScanfTests
|
||||
|
||||
self.each_test do |test, i|
|
||||
define_method("test_#{i}") do ||
|
||||
assert_equal(test[2], test[1].scanf(test[0]))
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
class TestIOScanf
|
||||
include Scanf
|
||||
extend ScanfTests
|
||||
|
||||
self.each_test do |test, i|
|
||||
define_method("test_#{i}") do ||
|
||||
Tempfile.create("iotest.dat") do |fh|
|
||||
fh.print test[1]
|
||||
fh.rewind
|
||||
assert_equal(test[2], fh.scanf(test[0]))
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -1,82 +0,0 @@
|
|||
# frozen_string_literal: false
|
||||
# $Id$
|
||||
#
|
||||
# scanf for Ruby
|
||||
#
|
||||
# Some not very comprehensive tests of block behavior.
|
||||
|
||||
|
||||
require 'test/unit'
|
||||
require 'scanf'
|
||||
require 'tmpdir'
|
||||
|
||||
class TestScanfBlock < Test::Unit::TestCase
|
||||
|
||||
def setup
|
||||
@str = <<-EOS
|
||||
Beethoven 1770
|
||||
Bach 1685
|
||||
Handel 1685
|
||||
Scarlatti 1685
|
||||
Brahms 1833
|
||||
EOS
|
||||
end
|
||||
|
||||
alias set_up setup
|
||||
def test_str1
|
||||
res = @str.scanf("%s%d") { |name, year| "#{name} was born in #{year}." }
|
||||
assert_equal(res,
|
||||
[ "Beethoven was born in 1770.",
|
||||
"Bach was born in 1685.",
|
||||
"Handel was born in 1685.",
|
||||
"Scarlatti was born in 1685.",
|
||||
"Brahms was born in 1833." ])
|
||||
end
|
||||
|
||||
def test_str2
|
||||
names = @str.scanf("%s%d") { |name, year| name.upcase }
|
||||
assert_equal(names, ["BEETHOVEN", "BACH", "HANDEL", "SCARLATTI", "BRAHMS"])
|
||||
end
|
||||
|
||||
def test_str3
|
||||
assert_equal("".scanf("%d%f%s") {}, [])
|
||||
end
|
||||
|
||||
def test_str4
|
||||
assert_equal("abc".scanf("%d%f%s") {}, [])
|
||||
end
|
||||
|
||||
def test_str5
|
||||
assert_equal("abc".scanf("") {}, [])
|
||||
end
|
||||
|
||||
def test_io1
|
||||
fn = "#{Dir.tmpdir}/iotest.dat.#{$$}"
|
||||
File.open(fn, "w") { |fh| fh.puts(@str) }
|
||||
fh = File.open(fn, "rb")
|
||||
res = fh.scanf("%s%d") { |name, year| "#{name} was born in #{year}." }
|
||||
|
||||
assert_equal(
|
||||
[ "Beethoven was born in 1770.",
|
||||
"Bach was born in 1685.",
|
||||
"Handel was born in 1685.",
|
||||
"Scarlatti was born in 1685.",
|
||||
"Brahms was born in 1833." ],res)
|
||||
fh.close
|
||||
ensure
|
||||
File.delete(fn)
|
||||
end
|
||||
|
||||
def test_io2
|
||||
fn = "#{Dir.tmpdir}/iotest.dat.#{$$}"
|
||||
File.open(fn, "w").close
|
||||
fh = File.open(fn,"rb")
|
||||
assert_equal(fh.scanf("") {}, [])
|
||||
fh.seek(0)
|
||||
assert_equal(fh.scanf("%d%f%s") {}, [])
|
||||
fh.close
|
||||
ensure
|
||||
File.delete(fn)
|
||||
end
|
||||
|
||||
end
|
|
@ -1,28 +0,0 @@
|
|||
# frozen_string_literal: false
|
||||
# $Id$
|
||||
#
|
||||
# scanf for Ruby
|
||||
#
|
||||
# Ad hoc tests of IO#scanf (needs to be expanded)
|
||||
|
||||
require 'test/unit'
|
||||
require 'scanf'
|
||||
|
||||
class TestScanfIO < Test::Unit::TestCase
|
||||
def test_io
|
||||
File.open(File.join(File.dirname(__FILE__), "data.txt"), "r") do |fh|
|
||||
assert_equal(0, fh.pos)
|
||||
assert_equal(["this", "is"], fh.scanf("%s%s"))
|
||||
assert_equal([33, "little"], fh.scanf("%da fun%s"))
|
||||
end
|
||||
end
|
||||
|
||||
def test_pipe_scanf
|
||||
IO.pipe do |r, w|
|
||||
w.write('a')
|
||||
w.close
|
||||
assert_equal([], r.scanf('a'))
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
@ -19,7 +19,6 @@
|
|||
# * https://github.com/ruby/date
|
||||
# * https://github.com/ruby/zlib
|
||||
# * https://github.com/ruby/fcntl
|
||||
# * https://github.com/ruby/scanf
|
||||
# * https://github.com/ruby/strscan
|
||||
# * https://github.com/ruby/ipaddr
|
||||
# * https://github.com/ruby/logger
|
||||
|
@ -62,7 +61,6 @@ $repositories = {
|
|||
date: 'ruby/date',
|
||||
zlib: 'ruby/zlib',
|
||||
fcntl: 'ruby/fcntl',
|
||||
scanf: 'ruby/scanf',
|
||||
strscan: 'ruby/strscan',
|
||||
ipaddr: 'ruby/ipaddr',
|
||||
logger: 'ruby/logger',
|
||||
|
@ -220,7 +218,7 @@ def sync_default_gems(gem)
|
|||
cp_r(Dir.glob("#{upstream}/ext/racc/cparse/*"), "ext/racc/cparse")
|
||||
cp_r("#{upstream}/test", "test/racc")
|
||||
`git checkout ext/racc/cparse/README`
|
||||
when "rexml", "rss", "matrix", "irb", "csv", "shell", "logger", "ostruct", "scanf", "webrick", "fileutils", "forwardable", "prime", "tracer", "ipaddr", "mutex_m", "sync"
|
||||
when "rexml", "rss", "matrix", "irb", "csv", "shell", "logger", "ostruct", "webrick", "fileutils", "forwardable", "prime", "tracer", "ipaddr", "mutex_m", "sync"
|
||||
sync_lib gem
|
||||
else
|
||||
end
|
||||
|
|
Loading…
Add table
Reference in a new issue