Rubyで必要な行だけファイルから取り出して複数行のソートするクラス作ってみた.


友人からの頼まれごと

かなりコアな題材で物理演算を行なっている友人がいて,その方からの頼まれごとで作って見ました.ご飯でもご馳走してもらおうかな

依頼内容

大まかに2つありました.

  • 計算機から複数のデータが羅列された一つのファイルが出力されるから,必要な分だけ取り出したい.
  • ファイルのソートを優先順位をつけて行いたい.

要は

datas.txt
-5.00 -4.00    9.77394    5.48712    3.61496       -57.3331870000   +1.0091838809
-2.00 -1.00   10.08259    5.65860    3.61496       -57.4409550000   +0.9363980334
-2.00 +1.00   10.08259    5.77291    3.61496       -57.3427100000   +0.9555686992
-3.00 -3.00    9.97971    5.54428    3.61496       -57.4680020000   +0.9448945625
-5.00 +0.00    9.77394    5.71575    3.61496       -57.4110870000   +0.9386141037
+0.00 +0.00   10.28836    5.71575    3.61496       -57.2935550000   +0.9841821532
-2.00 +0.00   10.08259    5.71575    3.61496       -57.3990650000   +0.9432751244

これのもっと長いファイルが計算機から出力されるから

xy_E_datas.txt
-5.0 -4.0 -57.333187
-5.0 -3.0 -57.377965
-5.0 -2.0 -57.405772
-5.0 -1.0 -57.41669
-5.0 +0.0 -57.411087
-5.0 +1.0 -57.389966

-4.0 -4.0 -57.404478
-4.0 -3.0 -57.434789
-4.0 -2.0 -57.449075

みたいな感じで綺麗にしていってよってお話です.

実装

コードをぺたり

sort_slab_data.rb
require "fileutils"
class SortSlabData

  def initialize
  end

  # get necessary lines from the file
  def need_lines(file, need_lines)
    new_lines = []
    File.open(file, "r+") do |file|
      file.each_line do |f|
        items = f.split
        items.clone.each_with_index.reverse_each do |line, index|
            items.delete_at(index) unless need_lines.include?(index)
        end
        new_lines << items.join(" ")
      end
    end
    new_lines
  end

  def sort_lines(lines, sort_lines)
    lines = lines.clone
    lines.clone.each_with_index do |line, index|
      lines[index] = all_to_f(line.split)
    end
    lines.sort_by! do |x|
      sort_items = []
      sort_lines.each do |line|
        sort_items << x[line]
      end
      sort_items
    end
    lines
  end

  # write_linesは指定の書式があったのでそれを遵守した書き方になっています.ここは万人向けではないので無視していいでしょう.
  def write_lines(file, lines)
    contents = []
    File.open(file, "w+") do |file|
      min = -(10**7)
      # format_arrangement
      lines.each do |line|
        if min < line[0]
          min = line[0]
          contents << "\n"
        end
        line.each_with_index do |item, index|
          line[index] = item.to_s
          line[index].insert(0, '+') if line[index][0] != '-'
        end
        contents << line.join(" ")
      end
      contents.shift if contents[0] == "\n"
      # file_write
      contents.each do |content|
        file.puts(content)
      end
    end
  end

  private

  def all_to_f(objs)
    objs.each_with_index do |obj, index|
          objs[index] = obj.to_f
    end
    objs
  end
end

# make x, y, energy file
file_path = ARGV[0]
physics_sort = SortSlabData.new
lines = physics_sort.need_lines(file_path, [0, 1, 5])
sorted_lines = physics_sort.sort_lines(lines, [0, 1])
physics_sort.write_lines("xy_E_#{file_path}", sorted_lines)

ファイルから必要な行だけ取り出して配列に入れる.

def need_lines(file, need_lines)で行なっています.
引数で与えらたfileには抜き出すファイルを指定.need_linesには配列で抜き出したい列番号を指定.
ex) file = 'datas.txt', need_lines = [0, 1, 5]

配列のソートを優先順位をつけて行う.

def sort_lines(lines, sort_lines)で行なっています.
引数で与えらたlinesにはソートする配列を指定.sort_linesには優先する順に要素番号を記入した配列を記載.
ex) lines = ["0 4 5", "8 9 10"], need_lines = [0, 1]

まとめ

結構限定的なお話なのでほとんどの人には参考にならないでしょうが,一応記事にまとめておきました.