Ugly code - suggestions?

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 :stuck_out_tongue:
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 :slight_smile:
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 :slight_smile: 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 :smiley:

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