如何阅读混合的Windows和Unix行结尾的每行上传文件?

时间:2013-09-24 14:32:55

标签: ruby-on-rails ruby file-io

我正在尝试在Rails中读取上传文件的每一行。

file_data = params[:files]
    if file_data.respond_to?(:read)
      file_data.read.gsub( /\n/, "\r\n" ).split("\r\n").each do |line|
        inputUsers.push(line.strip)
      end
    elsif file_data.respond_to?(:path)
      File.read(file_data.path).gsub( /\n/, "\r\n" ).split("\r\n").each do |line|
       inputUsers.push(line.strip)
     end

如果上传的文件包含Windows和Unix编码的混合,可能是由于从多个位置复制,Rails没有正确分隔文件的每一行,有时会返回两行作为一行。

该应用程序托管在Linux机器上。此外,该文件是从Google文档电子表格列中复制的。

这个问题有什么解决方案吗?


编辑:

未分隔成新行的行的十六进制代码如下所示:

636f 6d0d 0a4e 6968

3 个答案:

答案 0 :(得分:2)

以下是我如何做到这一点。首先,测试一些代码:

SAMPLE_TEXT = [
  "now\ris\r\nthe\ntime\n",
  "for all good men\n"
]

def read_file(data)
  data.each do |li|                       
    [ *li.split(/[\r\n]+/) ].each do |l|  
      yield l                             
    end                                   
  end                                     
end

read_file(SAMPLE_TEXT) do |li|
  puts li                       
end                             

哪个输出:

now
is
the
time
for all good men

神奇发生在[ *li.split(/[\r\n]+/) ]。打破它:

  • li.split(/[\r\n]+/)导致该行在返回,换行和其组合上被拆分。如果一行有多个代码,那么代码会吞噬空行,所以如果有可能你会收到那些你需要更复杂的模式,/[\r\n]{1,2}/虽然未经测试,但应该有效。
  • *li.split(/[\r\n]+/)使用“splat”运算符*,它表示将以下数组分解为其组成元素。当您不确定是否将单个元素或数组传递给方法时,这是获取数组的便捷方法。
  • [*li.split(/[\r\n]+/)]获取返回的组件并将其转回单个数组。

修改处理文件的方法很简单:

def read_file(fname)
  File.foreach(fname) do |li|
    [ *li.split(/[\r\n]+/) ].each do |l|
      yield l
    end
  end
end

以与上一个示例中相同的方式调用它:

read_file('path/to/file') do |li|
  puts li                       
end                             

您要使用foreach的原因是它会逐行读取,这比使用readreadlines诋毁文件的内存效率要高得多其中一次将整个文件读入内存。 foreach也非常快,因此在使用它时不会受到速度限制。因此,read - 类型方法几乎没有优势,使用foreach具有很大优势。

答案 1 :(得分:1)

您将\n替换为\r\n,这在解析Windows文件时会出现问题。现在\r\n变为\r\r\n

最好是替换Unix行结束格式,然后拆分\n

file_data.read.gsub( /\n/, "\r\n" ).split("\r\n").each do |line|

变为:

file_data.read.gsub( /\r\n/, "\n" ).split("\n").each do |line|

答案 2 :(得分:0)

尝试内置方法:

File.readlines('foo').each do |line|

或者:

File.open('foo').read.gsub(/\r\n?/, "\n").each_line do |line|