ruby Как прочитать файл и записать в другой файл, пропуская строки - PullRequest
0 голосов
/ 17 января 2020

У меня есть файл .txt, который я хочу скопировать в другой файл.
Вот как выглядит текст:

1
00:00:00 :  
renovation with a fully

2
00:00:01 :  
assembled 38-foot long trec

3
00:00:03 :  
skeleton, the exhibit offers a

4
00:00:04 :  
modern approach to presentation

5
00:00:07 :  
of more than 700 fossils with

6
00:00:08 :  
the exhibit starting in the

Я хочу, чтобы новый файл .txt выглядел как

00:00:00 :  renovation with a fully
00:00:01 :  assembled 38-foot long trec
00:00:03 :  skeleton, the exhibit offers a
00:00:04 :  modern approach to presentation
00:00:07 :  of more than 700 fossils with
00:00:08 :  the exhibit starting in the

Как я могу удалить номер строки и пустую строку и объединить строки с «хорошей» информацией
Как я могу это сделать?

Ответы [ 3 ]

2 голосов
/ 18 января 2020

Они делают его таким сложным ...

Ruby s foreach имеет возможность получить разделитель строк в качестве второго параметра:

foreach(name, sep=$/ [, getline_args, open_args]) {|line| block } → nil

Если вы передадите "\n\n" Ruby, будет возвращено несколько строк, разделенных пустой строкой. Это облегчает чтение файла, такого как ваш.

Использование ваших входных данных, сохраненных в файле, и запуск этого кода из той же директории:

File.foreach('test.txt', "\n\n") { |chunk|
  puts "%s %s" % chunk.lines[1, 2].map(&:chomp)
}

приводит к этому вывод на экран:

00:00:00 : renovation with a fully
00:00:01 : assembled 38-foot long trec
00:00:03 : skeleton, the exhibit offers a
00:00:04 : modern approach to presentation
00:00:07 : of more than 700 fossils with
00:00:08 : the exhibit starting in the

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

Если вы ДОЛЖНЫ иметь несколько пробелов, заканчивающихся двоеточием-разделителем:

:  r

против

: r

, строка формата может быть преобразована в "%s%s" или переключена на:

chunk.lines[1, 2].map(&:chomp).join

В результате:

chunk.lines[1, 2].map(&:chomp).join # => "00:00:00 :  renovation with a fully"

или

'%s%s' % chunk.lines[1, 2].map(&:chomp) # => "00:00:00 :  renovation with a fully"

Как записать второй файл, когда вы читаете из ввода, оставлено в качестве упражнения для вас.


Вот некоторые результаты теста для изумления:

require 'fruity'

test_file = <<EOT
1
00:00:00 :  
renovation with a fully
EOT

test_file.gsub(/\A\d+|\n/, '')               # => "00:00:00 :  renovation with a fully"
test_file.lines[1, 2].map(&:chomp).join      # => "00:00:00 :  renovation with a fully"
'%s%s' % test_file.lines[1, 2].map(&:chomp)  # => "00:00:00 :  renovation with a fully"

compare do
  cary { test_file.gsub(/\A\d+|\n/, '') }
  ttm1 { test_file.lines[1, 2].map(&:chomp).join }
  ttm2 { '%s%s' % test_file.lines[1, 2].map(&:chomp) }
end

# >> Running each test 4096 times. Test will take about 1 second.
# >> ttm1 is similar to ttm2
# >> ttm2 is faster than cary by 2x ± 0.1

__END__
1
00:00:00 :
renovation with a fully
1 голос
/ 18 января 2020

Давайте создадим входной файл. 1

fname_in  = 'in'

File.write fname_in, <<~BITTER_END
1
00:00:00 :  
renovation with a fully

2
00:00:01 :  
assembled 38-foot long trec

3
00:00:03 :  
skeleton, the exhibit offers a

4
00:00:04 :  
modern approach to presentation

5
00:00:07 :  
of more than 700 fossils with

6
00:00:08 :  
the exhibit starting in the
BITTER_END
  #=> 268

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

fname_out = 'out'

File.open(fname_out, 'w') do |f|
  File.foreach(fname_in, chomp: true).each_slice(4) {|_, line2, line3, _|
    f.puts line2 + line3}
end

Давайте подтвердим, что выходной файл был

puts File.read(fname_out)
00:00:00 :  renovation with a fully
00:00:01 :  assembled 38-foot long trec
00:00:03 :  skeleton, the exhibit offers a
00:00:04 :  modern approach to presentation
00:00:07 :  of more than 700 fossils with
00:00:08 :  the exhibit starting in the

Я использовал метод IO :: foreach , чтобы построчно прочитать входной файл. (Это должен быть метод go для чтения текстовых файлов построчно). Вы можете увидеть из foreach do c, что метод имеет две формы, одну с блоком и одну без. Я использовал последний, который возвращает перечислитель, потому что я хочу связать его с методом Enumerable # each_slice .

Давайте посмотрим, как работают эти перечислители.

enum1 = File.foreach(fname_in, chomp: true)
  #=> #<Enumerator: File:foreach("in", chomp: true)>
enum1.next #=> "1" 
enum1.next #=> "00:00:00 :  " 
enum1.next #=> "renovation with a fully" 
enum1.next #=> "" 
enum1.next #=> "2" 
...

Опция chomp: true заставляет foreach убирать символ новой строки в конце каждой строки. См. Перечислитель # следующий .

Далее мы имеем:

enum2 = enum1.each_slice(4)
  #=> #<Enumerator: #<Enumerator: File:foreach("in", chomp: true)
  #    >:each_slice(4)> 
enum2.next
  #=> ["1", "00:00:00 :  ", "renovation with a fully", ""] 
enum2.next
  #=> ["2", "00:00:01 :  ", "assembled 38-foot long trec", ""] 
...

Сравните возвращаемые значения для вычислений enum1 и enum2. Вы можете видеть, что enum2 можно рассматривать как составной перечислитель .

Элементы теперь генерируются с помощью enum2 и передаются в его блок, а значения присваиваются четырем блокам. переменные 2 :

line1, line2, line3, line4 = enum2.next
  #=> ["1", "00:00:00 :  ", "renovation with a fully", ""] 
line1 #=> "1" 
line2 #=> "00:00:00 :  " 
line3 #=> "renovation with a fully" 
line4 #=> "" line1

Этот процесс разбиения массива, возвращенного enum2.next, на четыре части называется Разложение массива . Это мощный и полезный инструмент, который может стать довольно сложным. 3

Теперь мы можем выполнить вычисление блока:

str = line2 + line3
  #=> "00:00:00 :  renovation with a fully"

, а затем f.puts str для записи эта строка в выходной файл. См. IO # приводит к .

Остальные вычисления, начиная с:

line1, line2, line3, line4 = enum2.next
  #=> ["2", "00:00:01 :  ", "assembled 38-foot long trec", ""] 

, аналогичны.

Обратите внимание, что я заменил блок переменные line1 и line4 с подчеркиванием ('_', допустимая локальная переменная), главным образом, чтобы сообщить читателю, что эти переменные не используются в расчете блока. Это обычная практика. Иногда вместо этого вы можете увидеть |_line1, line2, line3, _line4|, что означает то же самое.

Вот второй способ построения fname_out:

File.open(fname_out, 'w') do |f|
  File.foreach(fname_in, chomp: true) do |line|
    case line
    when /\A\d+\z/
      str = ''
    when ""
      f.puts str
    else
      str << line
    end
  end
end

Наконец, если файл не слишком большой, мы может просто проглотить его в память, используя File::read, а затем использовать String # scan с регулярным выражением, чтобы вытащить соответствующие строки, которые затем необходимо очистить, удалив символы новой строки.

File.read(fname_in).
     scan(r).
     map { |s| s.delete("\n") } 

, где регулярное выражение выглядит следующим образом:

r =
/
^         # match the beginning of a line
\d{2}:    # match two digits followed by a colon
.+?       # match one or more characters lazily
(?=\n\n)  # the match is to be followed by two newlines
/mx       # multiline and free-spacing regex definition modes

1. Для подтверждения правильности написания файла выполните puts File.read(fname_in). См. IO :: write и IO :: read . IO методы класса часто вызываются для класса File , как я это сделал. Это допустимо, потому что File.superclass #=> IO, поэтому File наследует методы IO.

2. Перед этим нам нужно выполнить enum2.rewind. См. Перечислитель # перемотка .

3. Если, например, у нас есть a, (b, (c, d)) = [1, [2, [3, 4]]], то a, b, c и d соответственно будут установлены равными 1, 2, 3 и 4.

1 голос
/ 17 января 2020
File.open("output.txt", "w") do |file_to_write|
  File.open("input.txt").each do |line|
    if $. % 4 == 2
      file_to_write.write(line.chomp)
    end
    if $. % 4 == 3
      file_to_write.write(line)
    end
  end
end

Ruby имеет специальный символ, который даст вам номер строки $., который можно заменить на File.open(filename).each_with_index do |line, index| и использовать индекс вместо $.. Если вы используете индекс, вам нужно добавить к нему 1, потому что он будет начинаться с 0.

Этот код построчно просматривает ваш входной файл. Строки, начинающиеся со второй и третьей строки, а затем каждой следующей интересующей строки, смещаются на 4. Как только мы находим первую половину, пишем эту строку в наш открытый файл без новой строки, как только мы находим вторую половину (вторая if оператор) записать эту строку с разделителем строки в файл.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...