Revision: 3464
Updated Code
at July 28, 2007 12:30 by Agent
Updated Code
require 'strscan'
class String
# Returns the string formatted according to a pattern.
#
# The pattern consists of placeholders and literals. The string is placed in
# the placeholders, leaving the literals as they are. The result may be
# truncated or padded if there are more placeholders than strings.
#
# Placeholders are '#' or '&'. Each '#' is replaced by one character from
# the string, or the filler character if the string has no characters left.
# The '&' is replaced by any remaining characters, or left out of the result
# if there are no remaining characters. There can only be one '&' in the
# pattern. If there is no '&' and more characters than placeholders, the
# remaining characters are discarded.
#
# '#' or '&' may be replaced by other characters if they are needed as
# literals.
#
# Examples:
# "123456789".using('###-##-####')
# => "123-45-6789"
# "12345".using('###-##-####')
# => "123-45"
# "12345".using('###-##-####', nil)
# => "12345"
# "12345".using('###-##-####', ' ')
# => "123-45- "
# "873555121276668".using ('(###) ###-#### ext &', '', true)
# => "(873) 555-1212 ext 76668"
# "8735551212".using ('(###) ###-#### ext &', '', true)
# => "(873) 555-1212"
# "5551212".using ('(###) ###-#### ext &', '', true)
# => "555-1212"
# "KB5774X".using ('##-&-#')
# => "KB-5774-X"
#
# Parameters:
# pattern -- The format string, see above.
# fill -- A string for padding. If the empty string, then the pattern is
# filled as much as possible, and the rest of the pattern is
# truncated. If nil, and the pattern cannot be filled exactly,
# the string is returned unchanged. If some other string, the
# pattern is filled as much as possible and the remainder is
# padded with the string. Defaults to the empty string.
# right -- If true, the pattern is filled from right-to-left instead of
# from left-to-right, and truncated on the left instead of the
# right if needed. Default is false.
# fixchar -- The single-character placeholder. Default is '#'.
# remchar -- The remaining-character placeholder. Default is '&'.
#
def using(pattern, fill='', right=false, fixchar='#', remchar='&')
remCount = pattern.count(remchar)
raise ArgumentError.new("Too many #{remchar}") if remCount > 1
raise ArgumentError.new("#{fixchar} too long") if fixchar.length > 1
raise ArgumentError.new("#{remchar} too long") if remchar.length > 1
raise ArgumentError.new("#{fill} too long") if fill.length > 1
remaining = remCount != 0
slots = pattern.count(fixchar)
# Return the string if it doesn't fit and we shouldn't even try,
if fill.nil?
return self if self.length < slots
return self if self.length > slots and !remaining
end
# Pad and clone the string if necessary.
source = if fill.nil? || fill.empty? then
self
elsif right then
self.rjust(slots, fill)
else
self.ljust(slots, fill)
end
# Truncate the string if necessary.
if source.length > slots && !remaining then
source = right ? source[-source.length, source.length] :
source[0, source.length]
end
# Truncate pattern if needed.
if !fill.nil? && fill.empty? then
if source.length < slots # implies '&' can be ignored
keepCount = source.length # Number of placeholders we are keeping
leftmost, rightmost = 0, pattern.length - 1
if right then
# Look right-to-left until we find the last '#' to keep.
# Loop starts at 1 because 0th placeholder is in the inject param.
leftmost = (1...keepCount).inject(pattern.rindex(fixchar)) {
|leftmost, n| pattern.rindex(fixchar, leftmost - 1) }
else
# Look left-to-right until we find the last '#' to keep.
rightmost = (1...keepCount).inject(pattern.index(fixchar)) {
|rightmost, n| pattern.index(fixchar, rightmost + 1) }
end
pattern = pattern[leftmost..rightmost]
slots = pattern.count(fixchar)
end
# Trim empty '&' up to nearest placeholder. If a '&' at the end goes
# empty, the literals between it and the nearest '#' are probably also
# unnecessary.
if source.length == slots then
if pattern.match("^#{Regexp.escape(remchar)}") then
pattern = pattern[pattern.index(fixchar) || 0 ... pattern.length]
elsif pattern.match("#{Regexp.escape(remchar)}$") then
pattern = pattern[0 ... (pattern.rindex(fixchar) + fixchar.length) || pattern.length]
end
end
end
# Figure out how long the remainder will be when we get to it.
remSize = source.length - slots
if remSize < 0 then remSize = 0; end
# Make the result.
scanner = ::StringScanner.new(pattern)
sourceIndex = 0
result = ''
fixRegexp = Regexp.new(Regexp.escape(fixchar))
remRegexp = Regexp.new(Regexp.escape(remchar))
while not scanner.eos?
if scanner.scan(fixRegexp) then
result += source[sourceIndex].chr
sourceIndex += 1
elsif scanner.scan(remRegexp) then
result += source[sourceIndex, remSize]
sourceIndex += remSize
else
result += scanner.getch
end
end
result
end
end
Revision: 3463
Updated Code
at July 28, 2007 12:21 by Agent
Updated Code
require 'strscan'
class String
# Returns the string formatted according to a pattern.
#
# The pattern consists of placeholders and literals. The string is placed in
# the placeholders, leaving the literals as they are. The result may be
# truncated or padded if there are more placeholders than strings.
#
# Placeholders are '#' or '&'. Each '#' is replaced by one character from
# the string, or the filler character if the string has no characters left.
# The '&' is replaced by any remaining characters, or left out of the result
# if there are no remaining characters. There can only be one '&' in the
# pattern. If there is no '&' and more characters than placeholders, the
# remaining characters are discarded.
#
# '#' or '&' may be replaced by other characters if they are needed as
# literals.
#
# Examples:
# "123456789".using('###-##-####')
# => "123-45-6789"
# "12345".using('###-##-####')
# => "123-45"
# "12345".using('###-##-####', nil)
# => "12345"
# "12345".using('###-##-####', ' ')
# => "123-45- "
# "5551212".using ('(###) ###-####', '', true)
# => "555-1212"
# "873555121276668".using ('(###) ###-#### ext &', '', true)
# => "(873) 555-1212 ext 76668"
# "8735551212".using ('(###) ###-#### ext &', '', true)
# => "(873) 555-1212"
# "5551212".using ('(###) ###-#### ext &', '', true)
# => "555-1212"
# "KB5774X".using ('##-&-#')
# => "KB-5774-X"
#
# Parameters:
# pattern -- The format string, see above.
# fill -- A string for padding. If the empty string, then the pattern is
# filled as much as possible, and the rest of the pattern is
# truncated. If nil, and the pattern cannot be filled exactly,
# the string is returned unchanged. If some other string, the
# pattern is filled as much as possible and the remainder is
# padded with the string. Defaults to the empty string.
# right -- If true, the pattern is filled from right-to-left instead of
# from left-to-right, and truncated on the left instead of the
# right if needed. Default is false.
# fixchar -- The single-character placeholder. Default is '#'.
# remchar -- The remaining-character placeholder. Default is '&'.
#
def using(pattern, fill='', right=false, fixchar='#', remchar='&')
remCount = pattern.count(remchar)
raise ArgumentError.new("Too many #{remchar}") if remCount > 1
raise ArgumentError.new("#{fixchar} too long") if fixchar.length > 1
raise ArgumentError.new("#{remchar} too long") if remchar.length > 1
raise ArgumentError.new("#{fill} too long") if fill.length > 1
remaining = remCount != 0
slots = pattern.count(fixchar)
# Return the string if it doesn't fit and we shouldn't even try,
if fill.nil?
return self if self.length < slots
return self if self.length > slots and !remaining
end
# Pad and clone the string if necessary.
source = if fill.nil? || fill.empty? then
self
elsif right then
self.rjust(slots, fill)
else
self.ljust(slots, fill)
end
# Truncate the string if necessary.
if source.length > slots && !remaining then
source = right ? source[-source.length, source.length] :
source[0, source.length]
end
# Truncate pattern if needed.
if !fill.nil? && fill.empty? then
if source.length < slots # implies '&' can be ignored
keepCount = source.length # Number of placeholders we are keeping
leftmost, rightmost = 0, pattern.length - 1
if right then
# Look right-to-left until we find the last '#' to keep.
# Loop starts at 1 because 0th placeholder is in the inject param.
leftmost = (1...keepCount).inject(pattern.rindex(fixchar)) {
|leftmost, n| pattern.rindex(fixchar, leftmost - 1) }
else
# Look left-to-right until we find the last '#' to keep.
rightmost = (1...keepCount).inject(pattern.index(fixchar)) {
|rightmost, n| pattern.index(fixchar, rightmost + 1) }
end
pattern = pattern[leftmost..rightmost]
slots = pattern.count(fixchar)
end
# Trim empty '&' up to nearest placeholder. If a '&' at the end goes
# empty, the literals between it and the nearest '#' are probably also
# unnecessary.
if source.length == slots then
if pattern.match("^#{Regexp.escape(remchar)}") then
pattern = pattern[pattern.index(fixchar) || 0 ... pattern.length]
elsif pattern.match("#{Regexp.escape(remchar)}$") then
pattern = pattern[0 ... (pattern.rindex(fixchar) + fixchar.length) || pattern.length]
end
end
end
# Figure out how long the remainder will be when we get to it.
remSize = source.length - slots
if remSize < 0 then remSize = 0; end
# Make the result.
scanner = ::StringScanner.new(pattern)
sourceIndex = 0
result = ''
fixRegexp = Regexp.new(Regexp.escape(fixchar))
remRegexp = Regexp.new(Regexp.escape(remchar))
while not scanner.eos?
if scanner.scan(fixRegexp) then
result += source[sourceIndex].chr
sourceIndex += 1
elsif scanner.scan(remRegexp) then
result += source[sourceIndex, remSize]
sourceIndex += remSize
else
result += scanner.getch
end
end
result
end
end
Revision: 3462
Initial Code
Initial URL
Initial Description
Initial Title
Initial Tags
Initial Language
at July 28, 2007 11:38 by Agent
Initial Code
require 'strscan'
class String
# Returns the string formatted according to a pattern.
#
# The pattern consists of placeholders and literals. The string is placed in
# the placeholders, leaving the literals as they are. The result may be
# truncated or padded if there are more placeholders than strings.
#
# Placeholders are '#' or '&'. Each '#' is replaced by one character from
# the string, or the filler character if the string has no characters left.
# The '&' is replaced by any remaining characters, or left out of the result
# if there are no remaining characters. There can only be one '&' in the
# pattern. If there is no '&' and more characters than placeholders, the
# remaining characters are discarded.
#
# '#' or '&' may be replaced by other characters if they are needed as
# literals.
#
# Examples:
# "123456789".using('###-##-####')
# => "123-45-6789"
# "12345".using('###-##-####')
# => "123-45"
# "12345".using('###-##-####', nil)
# => "12345"
# "12345".using('###-##-####', ' ')
# => "123-45- "
# "5551212".using ('(###) ###-####', '', true)
# => "555-1212"
# "873555121276668".using ('(###) ###-#### ext &', '', true)
# => "(873) 555-1212 ext 76668"
# "KB5774X".using ('##-&-#')
# => "KB-5774-X"
# "577K".using ('###-# stroke &')
# => "577-K"
#
# Parameters:
# pattern -- The format string, see above.
# fill -- A string for padding. If the empty string, then the pattern is
# filled as much as possible, and the rest of the pattern is
# truncated. If nil, and the pattern cannot be filled exactly,
# the string is returned unchanged. If some other string, the
# pattern is filled as much as possible and the remainder is
# padded with the string. Defaults to the empty string.
# right -- If true, the pattern is filled from right-to-left instead of
# from left-to-right, and truncated on the left instead of the
# right if needed. Default is false.
# fixchar -- The single-character placeholder. Default is '#'.
# remchar -- The remaining-character placeholder. Default is '&'.
#
def using(pattern, fill='', right=false, fixchar='#', remchar='&')
remCount = pattern.count(remchar)
raise ArgumentError.new("Too many #{remchar}") if remCount > 1
raise ArgumentError.new("#{fixchar} too long") if fixchar.length > 1
raise ArgumentError.new("#{remchar} too long") if remchar.length > 1
raise ArgumentError.new("#{fill} too long") if fill.length > 1
remaining = remCount != 0
slots = pattern.count(fixchar)
# Return the string if it doesn't fit and we shouldn't even try,
if fill.nil?
return self if self.length < slots
return self if self.length > slots and !remaining
end
# Pad and clone the string if necessary.
source = if fill.nil? || fill.empty? then
self
elsif right then
self.rjust(slots, fill)
else
self.ljust(slots, fill)
end
# Truncate the string if necessary.
if source.length > slots && !remaining then
source = right ? source[-source.length, source.length] :
source[0, source.length]
end
# Truncate pattern if needed.
if !fill.nil? && fill.empty? then
if source.length < slots # implies '&' can be ignored
keepCount = source.length # Number of placeholders we are keeping
leftmost, rightmost = 0, pattern.length - 1
if right then
# Look right-to-left until we find the last '#' to keep.
# Loop starts at 1 because 0th placeholder is in the inject param.
leftmost = (1...keepCount).inject(pattern.rindex(fixchar)) {
|leftmost, n| pattern.rindex(fixchar, leftmost - 1) }
else
# Look left-to-right until we find the last '#' to keep.
rightmost = (1...keepCount).inject(pattern.index(fixchar)) {
|rightmost, n| pattern.index(fixchar, rightmost + 1) }
end
pattern = pattern[leftmost..rightmost]
slots = pattern.count(fixchar)
end
# Trim empty '&' up to nearest placeholder. If a '&' at the end goes
# empty, the literals between it and the nearest '#' are probably also
# unnecessary.
if source.length == slots then
if pattern.match("^#{Regexp.escape(remchar)}") then
pattern = pattern[pattern.index(fixchar) || 0 ... pattern.length]
elsif pattern.match("#{Regexp.escape(remchar)}$") then
pattern = pattern[0 ... (pattern.rindex(fixchar) + fixchar.length) || pattern.length]
end
end
end
# Figure out how long the remainder will be when we get to it.
remSize = source.length - slots
if remSize < 0 then remSize = 0; end
# Make the result.
scanner = ::StringScanner.new(pattern)
sourceIndex = 0
result = ''
fixRegexp = Regexp.new(Regexp.escape(fixchar))
remRegexp = Regexp.new(Regexp.escape(remchar))
while not scanner.eos?
if scanner.scan(fixRegexp) then
result += source[sourceIndex].chr
sourceIndex += 1
elsif scanner.scan(remRegexp) then
result += source[sourceIndex, remSize]
sourceIndex += remSize
else
result += scanner.getch
end
end
result
end
end
Initial URL
Initial Description
This is an extension to Ruby's String class. Just put this code in a file and include it. This basically takes a string and adds literals and padding to it. For example, you can format a phone number with optional area code like this:
"5445556747".using('(###) ###-####', '', true)
=> (544) 555-6747
Initial Title
Format strings like PRINT USING
Initial Tags
format, ruby, text
Initial Language
Ruby