First ruby code, need hint on iterator

Hello,

Consider the following code which generates a hexdump of a string:

···

----------------------------------------------------------------------

#!/usr/bin/ruby

module Dump

        def hexdump

                x = 0
                offset = 0
                hex = ""
                str = ""
                out = ""

                each_byte { |c|
                        
                        x = x + 1

                        hex << sprintf("%02x ", c)
                        hex << "- " if(x == 8)
                        str << ((c >= 32 && c<=127) ? c.to_i.chr : ".")

                        if(x == 16)
                                out << sprintf("%08x: %-50s %s\n", offset, hex, str)
                                x = 0
                                hex = ""
                                str = ""
                                offset = offset + 16
                        end
                }

                out << "\n"

                return out
        end
end

l = $stdin.read(10000)
l.extend Dump
print l.hexdump

----------------------------------------------------------------------

This program has a serious flaw: only if 16 bytes are collected from the input,
a new line with hex codes is printed. If the input string has a length that is
not a multiple of 16, the remaining bytes will not be handled.

A solution would be to duplicate the line

  out << sprintf("%08x: %-50s %s\n", offset, hex, str)

somewhere at the end of the loop, so that any remaining buffers are printed.
This forces me to duplicate code, though, which is not what I want.

The obvious solution for me would be to change the line

  if(x == 16)

to something like

  if((x == 16) || (c.is_the_last_element_from_the_set))

The problem is in the fictional thingy 'is_the_last_element_from_the set',
which I believe is not available.

What would be the idiomatic solution for this in Ruby ? Any other comments on
the code are welcome as well, ofcourse

Thank you

This program has a serious flaw: only if 16 bytes are collected from the input,
a new line with hex codes is printed. If the input string has a length that is
not a multiple of 16, the remaining bytes will not be handled.

A solution would be to duplicate the line

        out << sprintf("%08x: %-50s %s\n", offset, hex, str)

I think it's ok to duplicate a single line. If you are worried about
the two lines
getting out of sync, put the line in a function:

def printHex(out, offset, hex, str)
  out << sprintf("%08x: %-50s %s\n", offset, hex, str)
end

Keep a count of total characters processed, and output if that is the
same as the length of your string: (see my embedded changes)

Gerr wrote:

Hello,

Consider the following code which generates a hexdump of a string:

----------------------------------------------------------------------

#!/usr/bin/ruby

module Dump

        def hexdump

                x = 0

                   num_chars = 0

                offset = 0
                hex = ""
                str = ""
                out = ""

                each_byte { |c|

                        x = x + 1

                           num_chars += 1

                        hex << sprintf("%02x ", c)
                        hex << "- " if(x == 8)
                        str << ((c >= 32 && c<=127) ? c.to_i.chr : ".")

                         if(x == 16) || num_chars >= length

···

                                out << sprintf("%08x: %-50s %s\n", offset, hex, str)
                                x = 0
                                hex = ""
                                str = ""
                                offset = offset + 16
                        end
                }

                out << "\n"

                return out
        end
end

l = $stdin.read(10000)
l.extend Dump
print l.hexdump

If you want to do things with 16 bytes at a time, you might as well
process them in groups of 16:

require 'enumerator'
module Dump
  def hexdump
    offset = 0
    out = ''
    split('').each_slice(16) {|chars|
    hc = chars.map{|c| "%02x " % c[0] }
    hex = hc[0..7] << '- ' << hc[8..15]
    str = chars.map{|c| (c[0] >= 32 && c[0] <= 127) ? c : "." }
    out << sprintf("%08x: %-50s %s\n", offset, hex, str)
    offset += 16
  }
  return out;
  end
end

or even:

module Dump
  def hexdump
    offset = -16
    split('').enum_slice(16).map {|chars|
      hc = chars.map{|c| "%02x " % c[0] }
    hex = hc[0..7] << '- ' << hc[8..15]
    str = chars.map{|c| (c[0] >= 32 && c[0] <= 127) ? c : "." }
    sprintf("%08x: %-50s %s\n", offset += 16, hex, str)
  }.join
  end
end

···

On 7/17/06, Gerr <gerald@frobble.com> wrote:

Hello,

Consider the following code which generates a hexdump of a string:
...
What would be the idiomatic solution for this in Ruby ? Any other comments on
the code are welcome as well, ofcourse

Delightful ! It's hard to think outside the box if you mainly have been
programming C for 20 years...

Thank you very much, I will learn a *lot* from this newsgroup, I'm sure
:slight_smile:

···

Sander Land <sander.land@gmail.com> wrote:

On 7/17/06, Gerr <gerald@frobble.com> wrote:

Hello,

Consider the following code which generates a hexdump of a string:
...
What would be the idiomatic solution for this in Ruby ? Any other comments on
the code are welcome as well, ofcourse

If you want to do things with 16 bytes at a time, you might as well
process them in groups of 16:

require 'enumerator'
module Dump
def hexdump
   offset = 0
   out = ''
   split('').each_slice(16) {|chars|
         hc = chars.map{|c| "%02x " % c[0] }
         hex = hc[0..7] << '- ' << hc[8..15]
         str = chars.map{|c| (c[0] >= 32 && c[0] <= 127) ? c : "." }
         out << sprintf("%08x: %-50s %s\n", offset, hex, str)
         offset += 16
       }
       return out;
end
end