Hello!
I’m writing an ANSI color codes parser which gets a string with text
and ANSI color codes and outputs an array with substrings and numbers
of the color codes.
A color code is something like “\e[31m”. Generally, I assume (am I
wrong?) they match /\e[(\d+)(;\d+)*m/.
Here is what I came up with:
module ANSIParser
def self.ansi_to_array(str)
str = str.to_s
ret = []
while ((m = /\e[(\d+)(;\d+)*m/.match(str)))
ret << m.pre_match if m.pre_match != “”
ret += m[0].scan(/\d+/).map { |c| c.to_i }
str = m.post_match
end
ret << str if str != “”
return ret
end
end
TEST:
ansistr = “\e[31mred,\e[32;1m\e[32;1mbold green\e[33;32;31mred
anyway, still bold\e[33;32;31m”
puts ansistr # you should see colors if your terminal supports ANSI
escapes
puts ANSIParser::ansi_to_array(ansistr).inspect
It works, but it doesn’t look much elegant. In fact, it looks much
more like perl than ruby to me
That while over there is awful. Is there a way to make it more
elegant? Is there some kind of iterator which does what my while
does? If not, would you split it somehow?
I’m looking for suggestions
Thanks!
couple of ideas for you- didn’t this out though
nil.to_s #=> “”
no need to re-scan a match if you can use $1, $2, etc
module ANSIParser
ANSI_MATCH = /\e[(\d+);(\d+)*m/
def self.ansi_to_array(str)
str = str.to_s
ret = []
str.scan(ANSI_MATCH) do |match|
ret << $`.to_s << $1.to_i << $2.to_i
end
ret << $'.to_s
end
end
sorry for using the Perl variables if you want you can use
Regexp.last_match.post_match
On 05.11.2006 22:13, Gabriele M. wrote:
return ret
end
end
TEST:
ansistr = “\e[31mred,\e[32;1m\e[32;1mbold green\e[33;32;31mred anyway,
still bold\e[33;32;31m”
puts ansistr # you should see colors if your terminal supports ANSI escapes
puts ANSIParser::ansi_to_array(ansistr).inspect
ansistr = “\e[31mred,\e[32;1m\e[32;1mbold green\e[33;32;31mred
anyway, still bold\e[33;32;31m”
=> “\e[31mred,\e[32;1m\e[32;1mbold green\e[33;32;31mred anyway, still
bold\e[33;32;31m”
ansistr.split(%r{(\e[\d+(?:;\d+)*m)}).inject([]) do |arr, s|
?> case s
when ""
arr
when %r{^\e}
arr.concat( s.scan( /\d+/ ).map {|x| x.to_i} )
else
?> arr << s
end
end
=> [31, “red,”, 32, 1, 32, 1, “bold green”, 33, 32, 31, “red anyway,
still bold”, 33, 32, 31]
Kind regards
robert
Il giorno 06/nov/06, alle ore 10:35, Robert K. ha scritto:
arr.concat( s.scan( /\d+/ ).map {|x| x.to_i} )
else
?> arr << s
end
end
=> [31, “red,”, 32, 1, 32, 1, “bold green”, 33, 32, 31, “red
anyway, still bold”, 33, 32, 31]
Kind regards
robert
Cool!
Thanks
HI –
On Mon, 6 Nov 2006, greg wrote:
couple of ideas for you- didn’t this out though
nil.to_s #=> “”
no need to re-scan a match if you can use $1, $2, etc
The reason is that you might have more than one ;\d+ sequence, and the
scan operation with captures will only give you the last one:
p “abc;31;32;33”.scan(/abc(;\d+)*/)
=> [[";33"]]
I don’t know whether the named captures in oniguruma might help
here…
David