如何在Ruby中编辑txt文件中的每x行数?

时间:2015-03-27 03:36:04

标签: ruby

我尝试使用Ruby更改文本文件中每个其他行的内容(以及一些文本文件,我需要每隔三行更改一些内容,依此类推。)

我发现this question有助于迭代每一行,但我特别需要帮助每x行更改一次。

###是我遇到问题的部分(迭代x行数。)

text = File.open('fr.txt').read
clean = ### .sub("\n", " ");
new = File.new("edit_fr.txt", "w")
new.puts clean
new.close

6 个答案:

答案 0 :(得分:4)

您可以使用下面的模数除法,其中n指的是您要处理的第n行,i指的是文件行的从0开始的索引。使用这两个值,模数学提供整数除法的余数,只要基于1的索引(i+1)是n的倍数,它就为0。

n = 3 # modify every 3rd line

File.open('edit_fr.txt','w') do |f|               # Open the output file
  File.open('fr.txt').each_with_index do |line,i| # Open the input file
    if (i+1) % n == 0                             # Every nth line
      f.print line.chomp                          # Remove newline
    else                                          # Every non-nth line
      f.puts line                                 # Print line
    end
  end
end

维基百科上提供了更多信息:http://en.wikipedia.org/wiki/Modulo_operation

  

在计算中,模运算在一个数除以另一个数之后找到余数(有时称为模数)。

     

给定两个正数,a(被除数)和n(除数),模n(缩写为mod n)是a乘以欧几里得分的余数。例如,表达式" 5 mod 2"将评估为1,因为5除以2得到2的商和1的余数,而" 9 mod 3"将评估为0,因为9乘3的除数为3,余数为0;在乘以3次3后,没有什么要从9中减去。(注意,用计算器进行除法不会显示此操作引用的结果;商将表示为小数。)

答案 1 :(得分:1)

您希望将输入文件的每一行写入输出文件,但是您希望在写入之前修改输入文件的每个nth行,从文件的第一行开始。

假设我们定义了一个方法modify,它接受​​一行文本作为参数并返回一个修改过的字符串。然后你就可以这样做:

def modify_and_write(in_fname, out_fname, n)
  enum = Array.new(n) { |i| i.zero? ? :process : :skip }.cycle
  f = File.open(out_fname, 'w')
  IO.foreach(in_fname) do |line|
    (line = process(line)) if enum.next == :process
    f.puts(line)
  end
  f.close
end

我一次只读一行(而不是使用IO#readlines)将整个文件读入数组中,以便它可以处理任何大小的文件。

假设:

n = 3

这里的关键是枚举器:

enum = Array.new(n) { |i| i.zero? ? :process : :skip }.cycle
  #=> #<Enumerator: [:process, :skip, :skip]:cycle> 
enum.next #=> :process 
enum.next #=> :skip 
enum.next #=> :skip 
enum.next #=> :process 
enum.next #=> :skip 
enum.next #=> :skip 
enum.next #=> :process 
enum.next #=> :skip
...

编辑:在回答之后我注意到了OP的评论:I need to combine every two lines: line1 /n line2 /n line3 /n line would become line1 space line2 /n line3 space line4,这与&#34不一致;我试图改变每个人的内容文本文件中的其他行&#34;。为了满足特定要求,我的解决方案可以修改如下:

def combine_lines(in_fname, out_fname, n)
  enum = Array.new(n) { |i| (i==n-1) ? :write : :read }.cycle
  f = File.open(out_fname, 'w')
  combined = []
  IO.foreach(in_fname) do |line|
    combined << line.chomp
    if enum.next == :write
      f.puts(combined.join(' '))
      combined.clear
    end
  end
  f.puts(combined.join(' ')) if combined.any?
  f.close
end

让我们试一试:

text =<<_
Now is
the time
for all
good
Rubyists
to do
something
other
than
code.
_

File.write('in',text)
combine_lines('in', 'out', 3)
puts File.read('out')
  # Now is the time for all
  # good Rubyists to do
  # something other than
  # code.

你也可以使用正则表达式,正如@Stefan所做的那样,这将是我对低于文件的偏好。这是另一个正则表达式实现:

def combine_lines(in_fname, out_fname, n)
  IO.write(out_fname,
    IO.read(in_fname)
      .scan(/(?:.*?\n){1,#{n}}/)
      .map { |s| s.split.join(' ') }
    )
end

combine_lines('in', 'out', 3)
puts File.read('out')
  # Now is the time for all
  # good Rubyists to do
  # something other than
  # code.

我们可以编写上述正则表达式,并将最终/更改为/x以包含注释:

r = /
 (?:       # begin a non-capture group
  .*?      # match any number of any character, non-greedily
  \n       # match (the first, because of non-greedily) end-of-line
  )        # end the non-capture group
  {1,#{n}} # match between 1 and n of the preceding non-capture group
/x

{1,#{n}}&#34;贪婪&#34;从某种意义上说,它将匹配尽可能多的行,直到n。如果行数始终是n的倍数,我们可以改写{{#n}},即匹配n非捕获组(即n行)。但是,如果行数不是n的倍数(如上例所示),我们需要{1,#{n}}来匹配最后一个非捕获组中的最后几行。

答案 2 :(得分:0)

every_other = 2

File.open('data.txt') do |f|
  e = f.each
  target_line = nil

  loop do
    every_other.times do
      target_line = e.next
    end

    puts target_line
  end
end

答案 3 :(得分:0)

我认为你只需要一个正则表达式就可以做到:

修改

好的,我知道我可以使用each_slice和一个简单的正则表达式来执行此操作:

def chop_it(file,num)
#file name and the number of lines to join
  arr = []
#create an empty array to hold the lines we create
  File.open(file) do |f|
#open your file into a `do..end` block, it closes automatically for you
    f.each_slice(num) do |slice|
#get an array of lines equal to num
      arr << slice.join(' ').gsub!(/\n/, '') + "\n"
#join the lines with ' ', then remove all the newlines and tack one
# on the end, adding the resulting line to the array.
    end
  end
  arr.join
#join all of the lines back into one string that can be sent to a file.
end

你有它,简单而灵活。只需输入文件名和您想要减少到一行的行数。即如果你想加入每两行,chop_it('data.txt',2)。每三个? chop_it('data.txt,3)

**旧答案**

old_text = File.read(data.txt)

new_text = old_text.gsub(/(?:(^.*)\n(^.*\n))/i,'\1 \2')

正则表达式将第一行与“\ n”匹配,第二行与“\ n”相匹配。替换返回两个匹配,它们之间有空格。

"this is line one\nthis is line two\n this is line three\nthis is line four]n"
\1 = "this is line one"
\2 = "this is line two\n"
'\1 \2' = "this is line one this is line two\n"

此正则表达式还将处理连续空白行中的每隔一个空白行

答案 4 :(得分:0)

new = File.new("edit_fr.txt", "w")
File.readlines("test.txt").each_slice(2) do |batch| # or each_slice(3) etc
  new.puts batch.map(&:chomp).join(" ")
end
new.close

答案 5 :(得分:0)

  

我需要组合每两行:line1 / n line2 / n line3 / n line将成为line1 space line2 / n line3 space line4

您可以将read整个文件转换为字符串,使用gsub!with_index将每个 n 换行符替换为空格write将替换的内容添加到新文件中:

content = IO.read('fr.txt')
content.gsub!("\n").with_index(1) { |m, i| (i % 2).zero? ? m : ' ' }
IO.write('edit-fr.txt', content)

输入fr.txt

line1
line2
line3
line4

输出edit-fr.txt

line1 line2
line3 line4