StringScanner

StringScanner performs lexical scanning operations on a string.

npm install StringScanner
2 573 downloads in the last day
12 203 downloads in the last week
52 187 downloads in the last month

StringScanner

StringScanner performs lexical scanning operations on a string. Inspired by Ruby's StringScanner class

How To Use

Instantiate a new StringScanner by passing its constructor the string to scan.

var StringScanner = require("StringScanner");
var ss = new StringScanner("abc123 def456");
// #<StringScanner 0/13 @ "abc12...">

For the following examples, ss represents the StringScanner instance defined above.

bol / beginningOfLine

Returns true if the scan pointer is at the beginning of a line (right after \n) or the beginning of the string, false otherwise.

ss.reset()    // #<StringScanner 0/13 @ "abc12...">
ss.pointer()  // 0
ss.bol()      // true
ss.scan(/./)  // "a"
ss.pointer()  // 1
ss.bol()      // false

captures

Returns an Array containing the contents of the capturing groups in the last evaluated pattern match.

ss.reset()              // #<StringScanner 0/13 @ "abc12...">
ss.check(/.*(..) (..)/  // "abc123 de"
ss.captures()           // ["23","de"]
ss.check(/\w+/)         // "abc123"
ss.captures()           // []
ss.check(/\s+/)         // null
ss.captures()           // []

check(pattern)

Note: this method alters last match results

Checks if a scan of the given pattern would succeed without advancing the scan pointer. Returns the portion of the string matched on successful match, null otherwise.

ss.reset()           // #<StringScanner 0/13 @ "abc12...">
ss.check(/[a-z]+/i)  // "abc"
ss.check(/[a-z]+/i)  // "abc"
ss.scan(/[a-z]+/i)   // "abc"
ss.check(/[a-z]+/i)  // null
ss.check(/[\d\s]+/)  // "123 "

checkUntil(pattern)

Note: this method alters last match results

Checks if a scanUntil would succeed without advancing the scan pointer. Returns the portion of the string being scanned from the scan pointer to the end of the matched string on successful match, null otherwise.

ss.reset()           // #<StringScanner 0/13 @ "abc12...">
ss.checkUntil(/\s/)  // "abc123 "
ss.checkUntil(/\s/)  // "abc123 "
ss.checkUntil(/r/)   // null
ss.scanUntil(/e/)    // "abc123 de"
ss.checkUntil(/\s/)  // null

clone / dup

Creates a duplicate of this instance of StringScanner.

concat(str)

Appends the given string to the string being scanned.

ss.reset()            // #<StringScanner 0/13 @ "abc12...">
ss.checkUntil(/h/)    // null
ss.concat(" ghi789")  // #<StringScanner 0/20 @ "abc12..."
ss.checkUntil(/h/)    // "abc123 def456 gh"

eos / endOfString

Returns true if the scan pointer is at the end of the string being scanned, false otherwise.

ss.reset()     // #<StringScanner 0/13 @ "abc12...">
ss.pointer()   // 0
ss.eos()       // false
ss.scan(/.*/)  // "abc123 def456"
ss.pointer()   // 13
ss.eos()       // true

exists(pattern) / exist(pattern)

Note: this method alters last match results

Warning: this method may return 0 on a successful operation. Use === comparision to null for failure check, for example: ss.exists(/a/i)===null

Checks if the given pattern matches anywhere after the current scan pointer. This will determine if a scanUntil operation will succeed. Returns the number of characters between the scan pointer and the position in which the match was found on success, null otherwise.

ss.reset()
// ----
ss.exists(/c/)   // 2
ss.match()       // "c"
ss.matched()     // true
// ----
ss.exists(/a/)   // 0
ss.match()       // "a"
ss.matched()     // true
// ----
ss.exists(/b*/)  // 0
ss.match()       // ""
ss.matched()     // true
// ----
ss.exists(/m/)   // null
ss.match()       // null
ss.matched()     // false

getch / getChar

Note: this method alters last match results

Note: Ruby equivalent: get_byte

scans one character and returns it; exactly equivalent to scan(/./).

ss.reset()    // #<StringScanner 0/13 @ "abc12...">
ss.getch()    // "a"
ss.reset()    // #<StringScanner 0/13 @ "abc12...">
ss.scan(/./)  // "a"

match

Note: Ruby equivalent: matched

Returns the last string matched or null if the last attempted match failed.

ss.reset()           // #<StringScanner 0/13 @ "abc12...">
ss.scan(/[a-z]+/i)   // "abc"
ss.match()           // "abc"
ss.check(/[a-z]+/i)  // null
ss.match()           // null

matches(pattern)

Note: Ruby equivalent: match?

Checks if a scan of the given pattern would succeed without advancing the scan pointer. Returns the length of the string matched on successful match, null otherwise.

ss.reset()             // #<StringScanner 0/13 @ "abc12...">
ss.matches(/[a-z]+/i)  // 3
ss.matches(/[a-z]+/i)  // 3
ss.scan(/[a-z]+/i)     // "abc"
ss.matches(/[a-z]+/i)  // null
ss.matches(/[\d\s]+/)  // 4

matched

Note: Ruby equivalent: matched?

Returns true if the last attempted match was successful, false otherwise.

ss.reset()      // #<StringScanner 0/13 @ "abc12...">
ss.scan(/\w+/)  // "abc123"
ss.matched()    // true
ss.scan(/\w+/)  // null
ss.matched()    // false

matchSize

Warning: this method may return 0 on a successful operation. Use === comparision to null for failure check, for example: ss.matchSize()===null

Returns the length of the most recently matched string if the most recent match attempt succeeded, null otherwise.

ss.reset()       // #<StringScanner 0/13 @ "abc12...">
ss.scan(/\w+/)   // "abc123"
ss.matchSize()   // 6
ss.check(/\w*/)  // ""
ss.matchSize()   // 0
ss.check(/\w+/)  // null
ss.matchSize()   // null

peek(len)

Returns len characters after the scan pointer, or the rest of the string, whichever is shorter.

ss.reset()      // #<StringScanner 0/13 @ "abc12...">
ss.scan(/.*d/)  // "abc123 d"
ss.peek(3)      // "ef4"
ss.peek(9001)   // "ef456"
ss.peek(0)      // ""
ss.peek(-3)     // ""

pointer / position

Returns the scan pointer position as an integer.

ss.reset()            // #<StringScanner 0/13 @ "abc12...">
ss.pointer()          // 0
ss.scan(/\w+\d+\s+/)  // "abc123 "
ss.pointer()          // 7
ss.scan([a-z]+)       // "def"
ss.pointer()          // 10

setPointer(pos)

Manually move the scan pointer to pos characters from the beginning of the string. The scan pointer is bounded between zero and the scanning string's length. Returns the position to which the scan pointer was moved. setPointer neither resets nor modifies the last match results.

ss.reset()         // #<StringScanner 0/13 @ "abc12...">
ss.setPointer(4)   // 4
ss.scan(/\d+/)     // "23"
ss.pointer()       // 6
ss.setPointer(-4)  // 0
ss.setPointer(99)  // 13

reset

Moves the scan pointer back to the beginning of the string being scanned and clears the last match results.

ss.reset()            // #<StringScanner 0/13 @ "abc12...">
ss.scanUntil(/(\s)/)  // "abc123 "
ss.pointer()          // 7
ss.match()            // "abc123 "
ss.captures()         // [" "]
ss.reset()            // #<StringScanner 0/13 @ "abc12...">
ss.pointer()          // 0
ss.match()            // null
ss.captures()         // []

rest

Returns the portion of the string being scanned after the scan pointer.

ss.reset()          // #<StringScanner 0/13 @ "abc12...">
ss.scanUntil(/\s/)  // "abc123 "
ss.rest()           // "def456"

scan(pattern)

Note: this method alters last match results

Attempts to match the given pattern at the position of the scan pointer. Returns the matched string and advances the string pointer upon successful match. A failed match will result in a null value being returned.

ss.reset()         // #<StringScanner 0/13 @ "abc12...">
ss.scan(/[a-z]+/)  // "abc"
ss                 // #<StringScanner 3/13 @ "abc12...">
ss.scan(/[a-z]+/)  // null
ss                 // #<StringScanner 3/13 @ "abc12...">
ss.scan(/[0-9]+/)  // "123"
ss                 // #<StringScanner 6/13 @ "abc12...">

scanUntil(pattern)

Note: this method alters last match results

Attempts to match the pattern against the string being scanned. On a successful match, the scan pointer is advanced to the end of the matched portion of the string and the portion of the string being scanned up to and including the matched string is returned. On a failed match, null is returned.

ss.reset()         // #<StringScanner 0/13 @ "abc12...">
ss.scanUntil(/ /)  // "abc123 "
ss.scanUntil(/f/)  // "def"
ss.scanUntil(/f/)  // null

skip(pattern)

Note: this method alters last match results

Performs a scan, returning the length of the matched string on successful match, null otherwise.

ss.reset()         // #<StringScanner 0/13 @ "abc12...">
ss.skip(/[a-z]+/)  // 3
ss.skip(/[a-z]+/)  // null
ss.skip(/[0-9]+/)  // 3

skipUntil(pattern)

Note: this method alters last match results

Performs a scanUntil, returning the length of the matched string on successful match, null otherwise.

ss.reset()         // #<StringScanner 0/13 @ "abc12...">
ss.skipUntil(/ /)  // 7
ss.skipUntil(/f/)  // 3
ss.skipUntil(/f/)  // null

string

Returns the entire string being scanned.

ss.string()  // "abc123 def456"
ss.getch()   // "a"
ss.string()  // "abc123 def456"

terminate / clear

Advances the scan pointer to the end of the string being scanned and resets the last match results.

ss.reset()      // #<StringScanner 0/13 @ "abc12...">
ss.getch()      // "a"
ss.pointer()    // 1
ss.terminate()  // #<StringScanner fin>
ss.pointer()    // 13
ss.eos()        // true
ss.match()      // null

Known Issues

Not really an issue, but StringScanner assumes the global flag (g) is disabled on any RegExp objects passed as patterns to any of its methods. If the global flag is enabled, StringScanner may produce unexpected results.

Additional Info

I am always open for feature requests or any feedback. I can be reached at Github.

Thanks to the Ruby community for the original idea and implementation.

npm loves you