Nicely formatting output to console, specifying number of tabs

假如想象 提交于 2019-12-18 10:38:12

问题


I am generating a script that is outputting information to the console. The information is some kind of statistic with a value. So much like a hash.

So one value's name may be 8 characters long and another is 3. when I am looping through outputting the information with two \t some of the columns aren't aligned correctly.

So for example the output might be as such:

long value name          14
short              12
little             13
tiny               123421
long name again          912421

I want all the values lined up correctly. Right now I am doing this:

puts "#{value_name} - \t\t #{value}"

How could I say for long names, to only use one tab? Or is there another solution?


回答1:


There is usually a %10s kind of printf scheme that formats nicely.
However, I have not used ruby at all, so you need to check that.


Yes, there is printf with formatting.
The above example should right align in a space of 10 chars.
You can format based on your widest field in the column.

printf ([port, ]format, arg...)

Prints arguments formatted according to the format like sprintf. If the first argument is the instance of the IO or its subclass, print redirected to that object. the default is the value of $stdout.




回答2:


Provided you know the maximum length to be no more than 20 characters:

printf "%-20s %s\n", value_name, value

If you want to make it more dynamic, something like this should work nicely:

longest_key = data_hash.keys.max_by(&:length)
data_hash.each do |key, value|
  printf "%-#{longest_key.length}s %s\n", key, value
end



回答3:


String has a built-in ljust for exactly this:

x = {"foo"=>37, "something long"=>42, "between"=>99}
x.each { |k, v| puts "#{k.ljust(20)} #{v}" }
# Outputs:
#  foo                  37
#  something long       42
#  between              99

Or, if you want tabs, you can do a little math (assuming tab display width of 8) and write a short display function:

def tab_pad(label, tab_stop = 4)
  label_tabs = label.length / 8
  label.ljust(label.length + tab_stop - label_tabs, "\t")
end

x.each { |k, v| puts "#{tab_pad(k)}#{v}" }
# Outputs: 
#  foo                  37
#  something long       42
#  between              99



回答4:


There was few bugs in it before, but now you can use most of printf syntax with % operator:

1.9.3-p194 :025 > " %-20s %05d" % ['hello', 12]
 => " hello                00012" 

Of course you can use precalculated width too:

1.9.3-p194 :030 > "%-#{width}s %05x" % ['hello', 12]
  => "hello          0000c" 



回答5:


I wrote a thing

  • Automatically detects column widths
  • Spaces with spaces
  • Array of arrays [[],[],...] or array of hashes [{},{},...]
  • Does not detect columns too wide for console window

    lists = [ [ 123, "SDLKFJSLDKFJSLDKFJLSDKJF" ], [ 123456, "ffff" ], ]

array_maxes

def array_maxes(lists)
  lists.reduce([]) do |maxes, list|
    list.each_with_index do |value, index|
      maxes[index] = [(maxes[index] || 0), value.to_s.length].max
    end
    maxes
  end
end

array_maxes(lists)
# => [6, 24]

puts_arrays_columns

def puts_arrays_columns(lists)
  maxes = array_maxes(hashes)
  lists.each do |list|
    list.each_with_index do |value, index|
      print " #{value.to_s.rjust(maxes[index])},"
    end
    puts
  end
end

puts_arrays_columns(lists)

# Output:
#     123, SDLKFJSLDKFJSLDKFJLSDKJF,
#  123456,                     ffff,

and another thing

hashes = [
  { "id" => 123,    "name" => "SDLKFJSLDKFJSLDKFJLSDKJF" },
  { "id" => 123456, "name" => "ffff" },
]

hash_maxes

def hash_maxes(hashes)
  hashes.reduce({}) do |maxes, hash|
    hash.keys.each do |key|
      maxes[key] = [(maxes[key] || 0), key.to_s.length].max
      maxes[key] = [(maxes[key] || 0), hash[key].to_s.length].max
    end
    maxes
  end
end

hash_maxes(hashes)
# => {"id"=>6, "name"=>24}

puts_hashes_columns

def puts_hashes_columns(hashes)
  maxes = hash_maxes(hashes)

  return if hashes.empty?

  # Headers
  hashes.first.each do |key, value|
    print " #{key.to_s.rjust(maxes[key])},"
  end
  puts

  hashes.each do |hash|
    hash.each do |key, value|
      print " #{value.to_s.rjust(maxes[key])},"
    end
    puts
  end

end

puts_hashes_columns(hashes)

# Output:
#      id,                     name,
#     123, SDLKFJSLDKFJSLDKFJLSDKJF,
#  123456,                     ffff,

Edit: Fixes hash keys considered in the length.

hashes = [
  { id: 123,    name: "DLKFJSDLKFJSLDKFJSDF", asdfasdf: :a  },
  { id: 123456, name: "ffff",                 asdfasdf: :ab },
]

hash_maxes(hashes)
# => {:id=>6, :name=>20, :asdfasdf=>8}

Want to whitelist columns columns?

hashes.map{ |h| h.slice(:id, :name) }
# => [
#  { id: 123,    name: "DLKFJSDLKFJSLDKFJSDF" },
#  { id: 123456, name: "ffff"                 },
#]



回答6:


For future reference and people who look at this or find it... Use a gem. I suggest https://github.com/wbailey/command_line_reporter




回答7:


You typically don't want to use tabs, you want to use spaces and essentially setup your "columns" your self or else you run into these types of problems.



来源:https://stackoverflow.com/questions/1087658/nicely-formatting-output-to-console-specifying-number-of-tabs

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!