Самый эффективный способ записи в файл с фиксированной шириной (Ruby) - PullRequest
1 голос
/ 12 мая 2010

В настоящее время я работаю с очень большими файлами фиксированной ширины, иногда более миллиона строк. Я написал метод, который может записывать поверх файлов на основе набора параметров, но я думаю, что должен быть более эффективный способ сделать это. Текущий код, который я использую:

def self.writefiles(file_name, positions, update_value)
@file_name = file_name
@positions = positions.to_i
@update_value = update_value

line_number = 0
@file_contents = File.open(@file_name, 'r').readlines

    while line_number < @file_contents.length
       @read_file_contents = @file_contents[line_number]
       @read_file_contents[@positions] = @update_value
       @file_contents[line_number] = @read_file_contents
       line_number += 1
    end

write_over_file = File.new(@file_name, 'w')
line_number = 0 

    while line_number < @file_contents.length
        write_over_file.write @file_contents[line_number]
        line_number += 1
    end

write_over_file.close
end

Например, если позиция 25 в файле указывает, что это оригинальный файл, значение будет установлено на «O», и если я захочу заменить это значение, я буду использовать ClassName.writefiles (filename, 140, «X» ) изменить эту позицию в каждой строке. Любая помощь в повышении эффективности этого метода будет принята с благодарностью!

Спасибо

Ответы [ 3 ]

1 голос
/ 12 мая 2010

Если это файл фиксированной ширины, вы можете открыть файл для чтения / записи и использовать поиск, чтобы перейти к началу данных, которые вы хотите записать, и записать только измененные данные, а не всю строку. Это, вероятно, будет более эффективным, чем перезапись всего файла для замены одного поля.

Вот грубый пример. Он читает последнее поле (10,20,30), увеличивает его на 1 и записывает обратно:

tha_file (10 символов в каждой строке, включая перевод строки)

12 3 x 10
23 4 x 20
78 9 x 30

seeker.rb

#!/usr/bin/env ruby
fh=open("tha_file", "r+")

$RECORD_WIDTH=10
$POS=8
$FIELD_WIDTH=2

# seek to first field
fh.seek($POS - 1, IO::SEEK_CUR)

while !fh.eof?

  cur_val=fh.read($FIELD_WIDTH).to_i
  puts "read #{cur_val}"
  fh.seek(-1 * $FIELD_WIDTH, IO::SEEK_CUR)
  cur_val = cur_val + 1

  fh.write(cur_val)
  puts "wrote #{cur_val}"

  # Move to start of next field in the middle of next record
  fh.seek($RECORD_WIDTH - $FIELD_WIDTH, IO::SEEK_CUR)
end
0 голосов
/ 12 мая 2010

Вы, безусловно, сэкономите некоторое время и довольно много памяти, переработав программы для считывания из файла строки за раз (вы сейчас читаете весь файл в память). Затем вы записываете в резервную копию файла внутри цикла, а затем переименовываете файл в конце. Как то так.

  def self.writefiles2(file_name, positions, update_value)
    @file_name = file_name
    @new_file = file_name + ".bak"
    @positions = positions.to_i
    @update_value = update_value

    line_number = 0
    reader = File.open(@file_name, 'r')
    writer = File.open(@new_file, 'w')

    while (line = reader.gets() and not line.nil? )
      line[@positions] = @update_value
      writer.puts(line)
    end
    reader.close
    writer.close
    # Rename the file
  end

Это, конечно, потребует некоторой обработки ошибок вокруг элемента переименования, что может привести к потере ваших входных данных.

0 голосов
/ 12 мая 2010
#!/usr/bin/ruby
# replace_at_pos.rb
pos, char, infile, outfile = $*
pos = pos.to_i
File.open(outfile, 'w') do |f|
  File.foreach(infile) do |line|
    line[pos] = char
    f.puts line
  end
end

и вы используете его как:

replace_at_pos.rb 140 X inputfile.txt outputfile.txt

Для замены набора значений вы можете использовать хеш:

replace = {
  100 => 'a',
  155 => 'c',
  151 => 't'
}
. . .
replace.each do |k, v|
  line[k] = v
end
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...