scanf 0.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (3) hide show
  1. checksums.yaml +7 -0
  2. data/lib/scanf.rb +776 -0
  3. metadata +87 -0
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: bf672bd5e82d5d049b39df5c6a2533b4659851db
4
+ data.tar.gz: cd371a3e521a39447bd8acf2b307205eb08a2a82
5
+ SHA512:
6
+ metadata.gz: 6fecc17f264ac5eafc4acb6e8aa8b1941f1f619bd32f120e2e9e1c6105bfdb436c1177503faa07d0945cddf20a4f164e634e63c4ffeace918742acb39d208425
7
+ data.tar.gz: 9d9b939de5f0148a737fc49bce4ca586f09406564bd5b62de045161eb29ef3e47ef40814f2db46a9c366c717ca1c5f9833dc03e72238a2cf25c1c58f0d2388c0
@@ -0,0 +1,776 @@
1
+ # frozen_string_literal: false
2
+ # scanf for Ruby
3
+ #
4
+ #--
5
+ # $Release Version: 1.1.2 $
6
+ # $Revision$
7
+ # $Id$
8
+ # $Author$
9
+ #++
10
+ #
11
+ # == Description
12
+ #
13
+ # scanf is an implementation of the C function scanf(3), modified as necessary
14
+ # for Ruby compatibility.
15
+ #
16
+ # the methods provided are String#scanf, IO#scanf, and
17
+ # Kernel#scanf. Kernel#scanf is a wrapper around STDIN.scanf. IO#scanf
18
+ # can be used on any IO stream, including file handles and sockets.
19
+ # scanf can be called either with or without a block.
20
+ #
21
+ # Scanf scans an input string or stream according to a <b>format</b>, as
22
+ # described below in Conversions, and returns an array of matches between
23
+ # the format and the input. The format is defined in a string, and is
24
+ # similar (though not identical) to the formats used in Kernel#printf and
25
+ # Kernel#sprintf.
26
+ #
27
+ # The format may contain <b>conversion specifiers</b>, which tell scanf
28
+ # what form (type) each particular matched substring should be converted
29
+ # to (e.g., decimal integer, floating point number, literal string,
30
+ # etc.) The matches and conversions take place from left to right, and
31
+ # the conversions themselves are returned as an array.
32
+ #
33
+ # The format string may also contain characters other than those in the
34
+ # conversion specifiers. White space (blanks, tabs, or newlines) in the
35
+ # format string matches any amount of white space, including none, in
36
+ # the input. Everything else matches only itself.
37
+ #
38
+ # Scanning stops, and scanf returns, when any input character fails to
39
+ # match the specifications in the format string, or when input is
40
+ # exhausted, or when everything in the format string has been
41
+ # matched. All matches found up to the stopping point are returned in
42
+ # the return array (or yielded to the block, if a block was given).
43
+ #
44
+ #
45
+ # == Basic usage
46
+ #
47
+ # require 'scanf'
48
+ #
49
+ # # String#scanf and IO#scanf take a single argument, the format string
50
+ # array = a_string.scanf("%d%s")
51
+ # array = an_io.scanf("%d%s")
52
+ #
53
+ # # Kernel#scanf reads from STDIN
54
+ # array = scanf("%d%s")
55
+ #
56
+ # == Block usage
57
+ #
58
+ # When called with a block, scanf keeps scanning the input, cycling back
59
+ # to the beginning of the format string, and yields a new array of
60
+ # conversions to the block every time the format string is matched
61
+ # (including partial matches, but not including complete failures). The
62
+ # actual return value of scanf when called with a block is an array
63
+ # containing the results of all the executions of the block.
64
+ #
65
+ # str = "123 abc 456 def 789 ghi"
66
+ # str.scanf("%d%s") { |num,str| [ num * 2, str.upcase ] }
67
+ # # => [[246, "ABC"], [912, "DEF"], [1578, "GHI"]]
68
+ #
69
+ # == Conversions
70
+ #
71
+ # The single argument to scanf is a format string, which generally
72
+ # includes one or more conversion specifiers. Conversion specifiers
73
+ # begin with the percent character ('%') and include information about
74
+ # what scanf should next scan for (string, decimal number, single
75
+ # character, etc.).
76
+ #
77
+ # There may be an optional maximum field width, expressed as a decimal
78
+ # integer, between the % and the conversion. If no width is given, a
79
+ # default of `infinity' is used (with the exception of the %c specifier;
80
+ # see below). Otherwise, given a field width of <em>n</em> for a given
81
+ # conversion, at most <em>n</em> characters are scanned in processing
82
+ # that conversion. Before conversion begins, most conversions skip
83
+ # white space in the input string; this white space is not counted
84
+ # against the field width.
85
+ #
86
+ # The following conversions are available.
87
+ #
88
+ # [%]
89
+ # Matches a literal `%'. That is, `%%' in the format string matches a
90
+ # single input `%' character. No conversion is done, and the resulting
91
+ # '%' is not included in the return array.
92
+ #
93
+ # [d]
94
+ # Matches an optionally signed decimal integer.
95
+ #
96
+ # [u]
97
+ # Same as d.
98
+ #
99
+ # [i]
100
+ # Matches an optionally signed integer. The integer is read in base
101
+ # 16 if it begins with `0x' or `0X', in base 8 if it begins with `0',
102
+ # and in base 10 other- wise. Only characters that correspond to the
103
+ # base are recognized.
104
+ #
105
+ # [o]
106
+ # Matches an optionally signed octal integer.
107
+ #
108
+ # [x, X]
109
+ # Matches an optionally signed hexadecimal integer,
110
+ #
111
+ # [a, e, f, g, A, E, F, G]
112
+ # Matches an optionally signed floating-point number.
113
+ #
114
+ # [s]
115
+ # Matches a sequence of non-white-space character. The input string stops at
116
+ # white space or at the maximum field width, whichever occurs first.
117
+ #
118
+ # [c]
119
+ # Matches a single character, or a sequence of <em>n</em> characters if a
120
+ # field width of <em>n</em> is specified. The usual skip of leading white
121
+ # space is suppressed. To skip white space first, use an explicit space in
122
+ # the format.
123
+ #
124
+ # [[]
125
+ # Matches a nonempty sequence of characters from the specified set
126
+ # of accepted characters. The usual skip of leading white space is
127
+ # suppressed. This bracketed sub-expression is interpreted exactly like a
128
+ # character class in a Ruby regular expression. (In fact, it is placed as-is
129
+ # in a regular expression.) The matching against the input string ends with
130
+ # the appearance of a character not in (or, with a circumflex, in) the set,
131
+ # or when the field width runs out, whichever comes first.
132
+ #
133
+ # === Assignment suppression
134
+ #
135
+ # To require that a particular match occur, but without including the result
136
+ # in the return array, place the <b>assignment suppression flag</b>, which is
137
+ # the star character ('*'), immediately after the leading '%' of a format
138
+ # specifier (just before the field width, if any).
139
+ #
140
+ # == scanf for Ruby compared with scanf in C
141
+ #
142
+ # scanf for Ruby is based on the C function scanf(3), but with modifications,
143
+ # dictated mainly by the underlying differences between the languages.
144
+ #
145
+ # === Unimplemented flags and specifiers
146
+ #
147
+ # * The only flag implemented in scanf for Ruby is '<tt>*</tt>' (ignore
148
+ # upcoming conversion). Many of the flags available in C versions of
149
+ # scanf(3) have to do with the type of upcoming pointer arguments, and are
150
+ # meaningless in Ruby.
151
+ #
152
+ # * The <tt>n</tt> specifier (store number of characters consumed so far in
153
+ # next pointer) is not implemented.
154
+ #
155
+ # * The <tt>p</tt> specifier (match a pointer value) is not implemented.
156
+ #
157
+ # === Altered specifiers
158
+ #
159
+ # [o, u, x, X]
160
+ # In scanf for Ruby, all of these specifiers scan for an optionally signed
161
+ # integer, rather than for an unsigned integer like their C counterparts.
162
+ #
163
+ # === Return values
164
+ #
165
+ # scanf for Ruby returns an array of successful conversions, whereas
166
+ # scanf(3) returns the number of conversions successfully
167
+ # completed. (See below for more details on scanf for Ruby's return
168
+ # values.)
169
+ #
170
+ # == Return values
171
+ #
172
+ # Without a block, scanf returns an array containing all the conversions
173
+ # it has found. If none are found, scanf will return an empty array. An
174
+ # unsuccessful match is never ignored, but rather always signals the end
175
+ # of the scanning operation. If the first unsuccessful match takes place
176
+ # after one or more successful matches have already taken place, the
177
+ # returned array will contain the results of those successful matches.
178
+ #
179
+ # With a block scanf returns a 'map'-like array of transformations from
180
+ # the block -- that is, an array reflecting what the block did with each
181
+ # yielded result from the iterative scanf operation. (See "Block
182
+ # usage", above.)
183
+ #
184
+ # == Current limitations and bugs
185
+ #
186
+ # When using IO#scanf under Windows, make sure you open your files in
187
+ # binary mode:
188
+ #
189
+ # File.open("filename", "rb")
190
+ #
191
+ # so that scanf can keep track of characters correctly.
192
+ #
193
+ # Support for character classes is reasonably complete (since it
194
+ # essentially piggy-backs on Ruby's regular expression handling of
195
+ # character classes), but users are advised that character class testing
196
+ # has not been exhaustive, and that they should exercise some caution
197
+ # in using any of the more complex and/or arcane character class
198
+ # idioms.
199
+ #
200
+ # == License and copyright
201
+ #
202
+ # Copyright:: (c) 2002-2003 David Alan Black
203
+ # License:: Distributed on the same licensing terms as Ruby itself
204
+ #
205
+ # == Warranty disclaimer
206
+ #
207
+ # This software is provided "as is" and without any express or implied
208
+ # warranties, including, without limitation, the implied warranties of
209
+ # merchantability and fitness for a particular purpose.
210
+ #
211
+ # == Credits and acknowledgements
212
+ #
213
+ # scanf was developed as the major activity of the Austin Ruby Codefest
214
+ # (Austin, Texas, August 2002).
215
+ #
216
+ # Principal author:: David Alan Black (mailto:dblack@superlink.net)
217
+ # Co-author:: Hal Fulton (mailto:hal9000@hypermetrics.com)
218
+ # Project contributors:: Nolan Darilek, Jason Johnston
219
+ #
220
+ # Thanks to Hal Fulton for hosting the Codefest.
221
+ #
222
+ # Thanks to Matz for suggestions about the class design.
223
+ #
224
+ # Thanks to Gavin Sinclair for some feedback on the documentation.
225
+ #
226
+ # The text for parts of this document, especially the Description and
227
+ # Conversions sections, above, were adapted from the Linux Programmer's
228
+ # Manual manpage for scanf(3), dated 1995-11-01.
229
+ #
230
+ # == Bugs and bug reports
231
+ #
232
+ # scanf for Ruby is based on something of an amalgam of C scanf
233
+ # implementations and documentation, rather than on a single canonical
234
+ # description. Suggestions for features and behaviors which appear in
235
+ # other scanfs, and would be meaningful in Ruby, are welcome, as are
236
+ # reports of suspicious behaviors and/or bugs. (Please see "Credits and
237
+ # acknowledgements", above, for email addresses.)
238
+
239
+ module Scanf
240
+ # :stopdoc:
241
+
242
+ # ==Technical notes
243
+ #
244
+ # ===Rationale behind scanf for Ruby
245
+ #
246
+ # The impetus for a scanf implementation in Ruby comes chiefly from the fact
247
+ # that existing pattern matching operations, such as Regexp#match and
248
+ # String#scan, return all results as strings, which have to be converted to
249
+ # integers or floats explicitly in cases where what's ultimately wanted are
250
+ # integer or float values.
251
+ #
252
+ # ===Design of scanf for Ruby
253
+ #
254
+ # scanf for Ruby is essentially a <format string>-to-<regular
255
+ # expression> converter.
256
+ #
257
+ # When scanf is called, a FormatString object is generated from the
258
+ # format string ("%d%s...") argument. The FormatString object breaks the
259
+ # format string down into atoms ("%d", "%5f", "blah", etc.), and from
260
+ # each atom it creates a FormatSpecifier object, which it
261
+ # saves.
262
+ #
263
+ # Each FormatSpecifier has a regular expression fragment and a "handler"
264
+ # associated with it. For example, the regular expression fragment
265
+ # associated with the format "%d" is "([-+]?\d+)", and the handler
266
+ # associated with it is a wrapper around String#to_i. scanf itself calls
267
+ # FormatString#match, passing in the input string. FormatString#match
268
+ # iterates through its FormatSpecifiers; for each one, it matches the
269
+ # corresponding regular expression fragment against the string. If
270
+ # there's a match, it sends the matched string to the handler associated
271
+ # with the FormatSpecifier.
272
+ #
273
+ # Thus, to follow up the "%d" example: if "123" occurs in the input
274
+ # string when a FormatSpecifier consisting of "%d" is reached, the "123"
275
+ # will be matched against "([-+]?\d+)", and the matched string will be
276
+ # rendered into an integer by a call to to_i.
277
+ #
278
+ # The rendered match is then saved to an accumulator array, and the
279
+ # input string is reduced to the post-match substring. Thus the string
280
+ # is "eaten" from the left as the FormatSpecifiers are applied in
281
+ # sequence. (This is done to a duplicate string; the original string is
282
+ # not altered.)
283
+ #
284
+ # As soon as a regular expression fragment fails to match the string, or
285
+ # when the FormatString object runs out of FormatSpecifiers, scanning
286
+ # stops and results accumulated so far are returned in an array.
287
+
288
+ class FormatSpecifier
289
+
290
+ attr_reader :re_string, :matched_string, :conversion, :matched
291
+
292
+ private
293
+
294
+ def skip; /^\s*%\*/.match(@spec_string); end
295
+
296
+ def extract_float(s)
297
+ return nil unless s &&! skip
298
+ if /\A(?<sign>[-+]?)0[xX](?<frac>\.\h+|\h+(?:\.\h*)?)[pP](?<exp>[-+]\d+)/ =~ s
299
+ f1, f2 = frac.split('.')
300
+ f = f1.hex
301
+ if f2
302
+ len = f2.length
303
+ if len > 0
304
+ f += f2.hex / (16.0 ** len)
305
+ end
306
+ end
307
+ (sign == ?- ? -1 : 1) * Math.ldexp(f, exp.to_i)
308
+ elsif /\A([-+]?\d+)\.([eE][-+]\d+)/ =~ s
309
+ ($1 << $2).to_f
310
+ else
311
+ s.to_f
312
+ end
313
+ end
314
+ def extract_decimal(s); s.to_i if s &&! skip; end
315
+ def extract_hex(s); s.hex if s &&! skip; end
316
+ def extract_octal(s); s.oct if s &&! skip; end
317
+ def extract_integer(s); Integer(s) if s &&! skip; end
318
+ def extract_plain(s); s unless skip; end
319
+
320
+ def nil_proc(s); nil; end
321
+
322
+ public
323
+
324
+ def to_s
325
+ @spec_string
326
+ end
327
+
328
+ def count_space?
329
+ /(?:\A|\S)%\*?\d*c|%\d*\[/.match(@spec_string)
330
+ end
331
+
332
+ def initialize(str)
333
+ @spec_string = str
334
+ h = '[A-Fa-f0-9]'
335
+
336
+ @re_string, @handler =
337
+ case @spec_string
338
+
339
+ # %[[:...:]]
340
+ when /%\*?(\[\[:[a-z]+:\]\])/
341
+ [ "(#{$1}+)", :extract_plain ]
342
+
343
+ # %5[[:...:]]
344
+ when /%\*?(\d+)(\[\[:[a-z]+:\]\])/
345
+ [ "(#{$2}{1,#{$1}})", :extract_plain ]
346
+
347
+ # %[...]
348
+ when /%\*?\[([^\]]*)\]/
349
+ yes = $1
350
+ if /^\^/.match(yes) then no = yes[1..-1] else no = '^' + yes end
351
+ [ "([#{yes}]+)(?=[#{no}]|\\z)", :extract_plain ]
352
+
353
+ # %5[...]
354
+ when /%\*?(\d+)\[([^\]]*)\]/
355
+ yes = $2
356
+ w = $1
357
+ [ "([#{yes}]{1,#{w}})", :extract_plain ]
358
+
359
+ # %i
360
+ when /%\*?i/
361
+ [ "([-+]?(?:(?:0[0-7]+)|(?:0[Xx]#{h}+)|(?:[1-9]\\d*)))", :extract_integer ]
362
+
363
+ # %5i
364
+ when /%\*?(\d+)i/
365
+ n = $1.to_i
366
+ s = "("
367
+ if n > 1 then s += "[1-9]\\d{1,#{n-1}}|" end
368
+ if n > 1 then s += "0[0-7]{1,#{n-1}}|" end
369
+ if n > 2 then s += "[-+]0[0-7]{1,#{n-2}}|" end
370
+ if n > 2 then s += "[-+][1-9]\\d{1,#{n-2}}|" end
371
+ if n > 2 then s += "0[Xx]#{h}{1,#{n-2}}|" end
372
+ if n > 3 then s += "[-+]0[Xx]#{h}{1,#{n-3}}|" end
373
+ s += "\\d"
374
+ s += ")"
375
+ [ s, :extract_integer ]
376
+
377
+ # %d, %u
378
+ when /%\*?[du]/
379
+ [ '([-+]?\d+)', :extract_decimal ]
380
+
381
+ # %5d, %5u
382
+ when /%\*?(\d+)[du]/
383
+ n = $1.to_i
384
+ s = "("
385
+ if n > 1 then s += "[-+]\\d{1,#{n-1}}|" end
386
+ s += "\\d{1,#{$1}})"
387
+ [ s, :extract_decimal ]
388
+
389
+ # %x
390
+ when /%\*?[Xx]/
391
+ [ "([-+]?(?:0[Xx])?#{h}+)", :extract_hex ]
392
+
393
+ # %5x
394
+ when /%\*?(\d+)[Xx]/
395
+ n = $1.to_i
396
+ s = "("
397
+ if n > 3 then s += "[-+]0[Xx]#{h}{1,#{n-3}}|" end
398
+ if n > 2 then s += "0[Xx]#{h}{1,#{n-2}}|" end
399
+ if n > 1 then s += "[-+]#{h}{1,#{n-1}}|" end
400
+ s += "#{h}{1,#{n}}"
401
+ s += ")"
402
+ [ s, :extract_hex ]
403
+
404
+ # %o
405
+ when /%\*?o/
406
+ [ '([-+]?[0-7]+)', :extract_octal ]
407
+
408
+ # %5o
409
+ when /%\*?(\d+)o/
410
+ [ "([-+][0-7]{1,#{$1.to_i-1}}|[0-7]{1,#{$1}})", :extract_octal ]
411
+
412
+ # %f
413
+ when /%\*?[aefgAEFG]/
414
+ [ '([-+]?(?:0[xX](?:\.\h+|\h+(?:\.\h*)?)[pP][-+]\d+|\d+(?![\d.])|\d*\.\d*(?:[eE][-+]?\d+)?))', :extract_float ]
415
+
416
+ # %5f
417
+ when /%\*?(\d+)[aefgAEFG]/
418
+ [ '(?=[-+]?(?:0[xX](?:\.\h+|\h+(?:\.\h*)?)[pP][-+]\d+|\d+(?![\d.])|\d*\.\d*(?:[eE][-+]?\d+)?))' +
419
+ "(\\S{1,#{$1}})", :extract_float ]
420
+
421
+ # %5s
422
+ when /%\*?(\d+)s/
423
+ [ "(\\S{1,#{$1}})", :extract_plain ]
424
+
425
+ # %s
426
+ when /%\*?s/
427
+ [ '(\S+)', :extract_plain ]
428
+
429
+ # %c
430
+ when /\s%\*?c/
431
+ [ "\\s*(.)", :extract_plain ]
432
+
433
+ # %c
434
+ when /%\*?c/
435
+ [ "(.)", :extract_plain ]
436
+
437
+ # %5c (whitespace issues are handled by the count_*_space? methods)
438
+ when /%\*?(\d+)c/
439
+ [ "(.{1,#{$1}})", :extract_plain ]
440
+
441
+ # %%
442
+ when /%%/
443
+ [ '(\s*%)', :nil_proc ]
444
+
445
+ # literal characters
446
+ else
447
+ [ "(#{Regexp.escape(@spec_string)})", :nil_proc ]
448
+ end
449
+
450
+ @re_string = '\A' + @re_string
451
+ end
452
+
453
+ def to_re
454
+ Regexp.new(@re_string,Regexp::MULTILINE)
455
+ end
456
+
457
+ def match(str)
458
+ @matched = false
459
+ s = str.dup
460
+ s.sub!(/\A\s+/,'') unless count_space?
461
+ res = to_re.match(s)
462
+ if res
463
+ @conversion = send(@handler, res[1])
464
+ @matched_string = @conversion.to_s
465
+ @matched = true
466
+ end
467
+ res
468
+ end
469
+
470
+ def letter
471
+ @spec_string[/%\*?\d*([a-z\[])/, 1]
472
+ end
473
+
474
+ def width
475
+ @spec_string[/%\*?(\d+)/, 1]&.to_i
476
+ end
477
+
478
+ def mid_match?
479
+ return false unless @matched
480
+ cc_no_width = letter == '[' &&! width
481
+ c_or_cc_width = (letter == 'c' || letter == '[') && width
482
+ width_left = c_or_cc_width && (matched_string.size < width)
483
+
484
+ return width_left || cc_no_width
485
+ end
486
+
487
+ end
488
+
489
+ class FormatString
490
+
491
+ attr_reader :string_left, :last_spec_tried,
492
+ :last_match_tried, :matched_count, :space
493
+
494
+ SPECIFIERS = 'diuXxofFeEgGscaA'
495
+ REGEX = /
496
+ # possible space, followed by...
497
+ (?:\s*
498
+ # percent sign, followed by...
499
+ %
500
+ # another percent sign, or...
501
+ (?:%|
502
+ # optional assignment suppression flag
503
+ \*?
504
+ # optional maximum field width
505
+ \d*
506
+ # named character class, ...
507
+ (?:\[\[:\w+:\]\]|
508
+ # traditional character class, or...
509
+ \[[^\]]*\]|
510
+ # specifier letter.
511
+ [#{SPECIFIERS}])))|
512
+ # or miscellaneous characters
513
+ [^%\s]+/ix
514
+
515
+ def initialize(str)
516
+ @specs = []
517
+ @i = 1
518
+ s = str.to_s
519
+ return unless /\S/.match(s)
520
+ @space = true if /\s\z/.match(s)
521
+ @specs.replace s.scan(REGEX).map {|spec| FormatSpecifier.new(spec) }
522
+ end
523
+
524
+ def to_s
525
+ @specs.join('')
526
+ end
527
+
528
+ def prune(n=matched_count)
529
+ n.times { @specs.shift }
530
+ end
531
+
532
+ def spec_count
533
+ @specs.size
534
+ end
535
+
536
+ def last_spec
537
+ @i == spec_count - 1
538
+ end
539
+
540
+ def match(str)
541
+ accum = []
542
+ @string_left = str
543
+ @matched_count = 0
544
+
545
+ @specs.each_with_index do |spec,i|
546
+ @i=i
547
+ @last_spec_tried = spec
548
+ @last_match_tried = spec.match(@string_left)
549
+ break unless @last_match_tried
550
+ @matched_count += 1
551
+
552
+ accum << spec.conversion
553
+
554
+ @string_left = @last_match_tried.post_match
555
+ break if @string_left.empty?
556
+ end
557
+ return accum.compact
558
+ end
559
+ end
560
+ # :startdoc:
561
+ end
562
+
563
+ class IO
564
+
565
+ #:stopdoc:
566
+ # The trick here is doing a match where you grab one *line*
567
+ # of input at a time. The linebreak may or may not occur
568
+ # at the boundary where the string matches a format specifier.
569
+ # And if it does, some rule about whitespace may or may not
570
+ # be in effect...
571
+ #
572
+ # That's why this is much more elaborate than the string
573
+ # version.
574
+ #
575
+ # For each line:
576
+ #
577
+ # Match succeeds (non-emptily)
578
+ # and the last attempted spec/string sub-match succeeded:
579
+ #
580
+ # could the last spec keep matching?
581
+ # yes: save interim results and continue (next line)
582
+ #
583
+ # The last attempted spec/string did not match:
584
+ #
585
+ # are we on the next-to-last spec in the string?
586
+ # yes:
587
+ # is fmt_string.string_left all spaces?
588
+ # yes: does current spec care about input space?
589
+ # yes: fatal failure
590
+ # no: save interim results and continue
591
+ # no: continue [this state could be analyzed further]
592
+ #
593
+ #:startdoc:
594
+
595
+ # Scans the current string until the match is exhausted,
596
+ # yielding each match as it is encountered in the string.
597
+ # A block is not necessary though, as the results will simply
598
+ # be aggregated into the final array.
599
+ #
600
+ # "123 456".block_scanf("%d")
601
+ # # => [123, 456]
602
+ #
603
+ # If a block is given, the value from that is returned from
604
+ # the yield is added to an output array.
605
+ #
606
+ # "123 456".block_scanf("%d") do |digit,| # the ',' unpacks the Array
607
+ # digit + 100
608
+ # end
609
+ # # => [223, 556]
610
+ #
611
+ # See Scanf for details on creating a format string.
612
+ #
613
+ # You will need to require 'scanf' to use IO#scanf.
614
+ def scanf(str,&b) #:yield: current_match
615
+ return block_scanf(str,&b) if b
616
+ return [] unless str.size > 0
617
+
618
+ start_position = pos rescue 0
619
+ matched_so_far = 0
620
+ source_buffer = ""
621
+ result_buffer = []
622
+ final_result = []
623
+
624
+ fstr = Scanf::FormatString.new(str)
625
+
626
+ loop do
627
+ if eof || (tty? &&! fstr.match(source_buffer))
628
+ final_result.concat(result_buffer)
629
+ break
630
+ end
631
+
632
+ source_buffer << gets
633
+
634
+ current_match = fstr.match(source_buffer)
635
+
636
+ spec = fstr.last_spec_tried
637
+
638
+ if spec.matched
639
+ if spec.mid_match?
640
+ result_buffer.replace(current_match)
641
+ next
642
+ end
643
+
644
+ elsif (fstr.matched_count == fstr.spec_count - 1)
645
+ if /\A\s*\z/.match(fstr.string_left)
646
+ break if spec.count_space?
647
+ result_buffer.replace(current_match)
648
+ next
649
+ end
650
+ end
651
+
652
+ final_result.concat(current_match)
653
+
654
+ matched_so_far += source_buffer.size
655
+ source_buffer.replace(fstr.string_left)
656
+ matched_so_far -= source_buffer.size
657
+ break if fstr.last_spec
658
+ fstr.prune
659
+ end
660
+
661
+ begin
662
+ seek(start_position + matched_so_far, IO::SEEK_SET)
663
+ rescue Errno::ESPIPE
664
+ end
665
+
666
+ soak_up_spaces if fstr.last_spec && fstr.space
667
+
668
+ return final_result
669
+ end
670
+
671
+ private
672
+
673
+ def soak_up_spaces
674
+ c = getc
675
+ ungetc(c) if c
676
+ until eof ||! c || /\S/.match(c.chr)
677
+ c = getc
678
+ end
679
+ ungetc(c) if (c && /\S/.match(c.chr))
680
+ end
681
+
682
+ def block_scanf(str)
683
+ final = []
684
+ # Sub-ideal, since another FS gets created in scanf.
685
+ # But used here to determine the number of specifiers.
686
+ fstr = Scanf::FormatString.new(str)
687
+ last_spec = fstr.last_spec
688
+ begin
689
+ current = scanf(str)
690
+ break if current.empty?
691
+ final.push(yield(current))
692
+ end until eof || fstr.last_spec_tried == last_spec
693
+ return final
694
+ end
695
+ end
696
+
697
+ class String
698
+
699
+ # :section: scanf
700
+ #
701
+ # You will need to require 'scanf' to use these methods
702
+
703
+ # Scans the current string. If a block is given, it
704
+ # functions exactly like block_scanf.
705
+ #
706
+ # arr = "123 456".scanf("%d%d")
707
+ # # => [123, 456]
708
+ #
709
+ # require 'pp'
710
+ #
711
+ # "this 123 read that 456 other".scanf("%s%d%s") {|m| pp m}
712
+ #
713
+ # # ["this", 123, "read"]
714
+ # # ["that", 456, "other"]
715
+ # # => [["this", 123, "read"], ["that", 456, "other"]]
716
+ #
717
+ # See Scanf for details on creating a format string.
718
+ #
719
+ # You will need to require 'scanf' to use String#scanf
720
+ def scanf(fstr,&b) #:yield: current_match
721
+ if b
722
+ block_scanf(fstr,&b)
723
+ else
724
+ fs =
725
+ if fstr.is_a? Scanf::FormatString
726
+ fstr
727
+ else
728
+ Scanf::FormatString.new(fstr)
729
+ end
730
+ fs.match(self)
731
+ end
732
+ end
733
+
734
+ # Scans the current string until the match is exhausted
735
+ # yielding each match as it is encountered in the string.
736
+ # A block is not necessary as the results will simply
737
+ # be aggregated into the final array.
738
+ #
739
+ # "123 456".block_scanf("%d")
740
+ # # => [123, 456]
741
+ #
742
+ # If a block is given, the value from that is returned from
743
+ # the yield is added to an output array.
744
+ #
745
+ # "123 456".block_scanf("%d) do |digit,| # the ',' unpacks the Array
746
+ # digit + 100
747
+ # end
748
+ # # => [223, 556]
749
+ #
750
+ # See Scanf for details on creating a format string.
751
+ #
752
+ # You will need to require 'scanf' to use String#block_scanf
753
+ def block_scanf(fstr) #:yield: current_match
754
+ fs = Scanf::FormatString.new(fstr)
755
+ str = self.dup
756
+ final = []
757
+ begin
758
+ current = str.scanf(fs)
759
+ final.push(yield(current)) unless current.empty?
760
+ str = fs.string_left
761
+ end until current.empty? || str.empty?
762
+ return final
763
+ end
764
+ end
765
+
766
+ module Kernel
767
+ private
768
+ # Scans STDIN for data matching +format+. See IO#scanf for details.
769
+ #
770
+ # See Scanf for details on creating a format string.
771
+ #
772
+ # You will need to require 'scanf' to use Kernel#scanf.
773
+ def scanf(format, &b) #:doc:
774
+ STDIN.scanf(format ,&b)
775
+ end
776
+ end
metadata ADDED
@@ -0,0 +1,87 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: scanf
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ platform: ruby
6
+ authors:
7
+ - David Alan Black
8
+ autorequire:
9
+ bindir: exe
10
+ cert_chain: []
11
+ date: 2017-05-11 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: bundler
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: '1.14'
20
+ type: :development
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: '1.14'
27
+ - !ruby/object:Gem::Dependency
28
+ name: rake
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: '10.0'
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: '10.0'
41
+ - !ruby/object:Gem::Dependency
42
+ name: test-unit
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - ">="
46
+ - !ruby/object:Gem::Version
47
+ version: '0'
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - ">="
53
+ - !ruby/object:Gem::Version
54
+ version: '0'
55
+ description: scanf is an implementation of the C function scanf(3).
56
+ email:
57
+ - dblack@superlink.net
58
+ executables: []
59
+ extensions: []
60
+ extra_rdoc_files: []
61
+ files:
62
+ - lib/scanf.rb
63
+ homepage: https://github.com/ruby/scanf
64
+ licenses:
65
+ - BSD-2-Clause
66
+ metadata: {}
67
+ post_install_message:
68
+ rdoc_options: []
69
+ require_paths:
70
+ - lib
71
+ required_ruby_version: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - ">="
74
+ - !ruby/object:Gem::Version
75
+ version: 2.5.0dev
76
+ required_rubygems_version: !ruby/object:Gem::Requirement
77
+ requirements:
78
+ - - ">="
79
+ - !ruby/object:Gem::Version
80
+ version: '0'
81
+ requirements: []
82
+ rubyforge_project:
83
+ rubygems_version: 2.6.12
84
+ signing_key:
85
+ specification_version: 4
86
+ summary: scanf is an implementation of the C function scanf(3).
87
+ test_files: []