ruby 以\r、\n或\r\n作为行分隔符逐行读取文件

3wabscal  于 11个月前  发布在  Ruby
关注(0)|答案(2)|浏览(122)

我想逐行处理文件。但是,这些文件有不同的行分隔符:"\r""\n""\r\n"。我不知道它们使用的是哪一种或来自哪种操作系统。
我有两个解决方案:
1.使用bash命令将这些分隔符转换为"\n"

cat file |
tr '\r\n' '\n' |
tr '\r' '\n' |
ruby process.rb

字符串
1.读取整个文件并gsub这些分隔符

text=File.open('xxx.txt').read
text.gsub!(/\r\n?/, "\n")
text.each_line do |line|
  do some thing
end


但是当文件很大的时候,第二种解决方案并不好。参见reference。还有其他ruby惯用的高效解决方案吗?

uajslkp6

uajslkp61#

我建议您首先确定行分隔符。我假设您可以通过阅读字符直到遇到“\n”或“\r”(或者到达文件末尾,这时我们可以把“\n”当作行分隔符)。如果找到字符“\n”,我假设它是分隔符;如果找到“\r”,则尝试读取下一个字符。如果可以读取并且是“\n”,则返回“\r\n”作为分隔符。如果“\r”是文件中的最后一个字符或后跟“\n”以外的字符,则返回“\r”作为分隔符。

def separator(fname)
  f = File.open(fname)
  enum = f.each_char
  c = enum.next
  loop do
    case c[/\r|\n/]
    when "\n" then break
    when "\r"
      c << "\n" if enum.peek=="\n"
      break
    end
    c = enum.next
  end
  c[0][/\r|\n/] ? c : "\n"
end

字符串
然后逐行处理文件

def process(fname)
  sep = separator(fname)
  IO.foreach(fname, sep) { |line| puts line }
end


我还没有将"\r""\r\n"转换为"\n",当然你可以很容易地做到这一点,只要打开一个文件进行写入,在process中读取每一行,并使用默认的行分隔符将其写入输出文件。
让我们尝试一下(为了清楚起见,我显示了separator返回的值):

fname = "temp"

IO.write(fname, "slash n line 1\nslash n line 2\n")
  #=> 30 
separator(fname)                                    
  #=> "\n" 
process(fname)
  # slash n line 1
  # slash n line 2

IO.write(fname, "slash r line 1\rslash r line 2\r", )
  #=> 30 
separator(fname)
  #=> "\r" 
process(fname)
  # slash r line 1
  # slash r line 2

IO.write(fname, "slash r slash n line 1\r\nslash r slash n line 2\r\n")
  #=> 48 
separator(fname)
  #=> "\r\n" 
process(fname)
  # slash r slash n line 1
  # slash r slash n line 2

368yc8dk

368yc8dk2#

使用index函数(它给出字符串中第一个出现的子字符串,如果没有找到则返回nil):

line_terminator = nil
if text.index("\r\n")
  puts 'Windows'
  line_terminator = "\r\n"
end
if line_terminator.nil? && text.index("\n")
  puts 'Unix'
  line_terminator = "\n"
end
line_terminator = "\r" if line_terminator.nil? # else it's legacy Mac, i.e. "\r"

字符串
我不确定非常大的文件的性能,但它工作,它的漂亮和短。

相关问题