1

我正在使用这种方法来处理一个大约有 220,000 行的文本文件。处理一个需要几分钟,但我有很多。有什么建议可以加快这个过程吗?

def parse_list(file_path,import=false)
# Parse the fixed-length fields
   if File.exist?(file_path)
     result=[]
     File.readlines(file_path)[5..-1].each do |rs|
        if rs.length > 140
          r=rs.strip
          unless r=='' 
            filing={
                  'name' => r[0..50].strip,
                  'form' => r[51..70].strip,
                  'type'  => r[71..80].strip,
                  'date' => r[81..90].strip,
                  'location' => r[91..-1].strip
                  }     
              result.push(filing)
          end
        end
     end
     return result
   else
     return false
   end
end

更新:

最初,我认为使用 Nex 和 thetinman 的方法可以节省大量时间,所以我继续测试它们,以保持解析方法的一致性。

使用我原来r[].strip的解析方法,但使用 Nex 的each_lineblock 方法和 thetinman 的foreach方法:

Rehearsal ---------------------------------------------
Nex         8.260000   0.130000   8.390000 (  8.394067)
Thetinman   9.740000   0.120000   9.860000 (  9.862880)
----------------------------------- total: 18.250000sec

                user     system      total        real
Nex        14.270000   0.140000  14.410000 ( 14.397286)
Thetinman  19.030000   0.080000  19.110000 ( 19.118621)

使用thetinman的unpack.map解析方法再次运行:

Rehearsal ---------------------------------------------
Nex         9.580000   0.120000   9.700000 (  9.694327)
Thetinman  11.470000   0.090000  11.560000 ( 11.567294)
----------------------------------- total: 21.260000sec

                user     system      total        real
Nex        15.480000   0.120000  15.600000 ( 15.599319)
Thetinman  18.150000   0.070000  18.220000 ( 18.217744)

unpack.map(&:strip)vs r[].stripunpackwithmap似乎并没有提高速度,但是将来会使用它是一种有趣的方法。

我发现了一个不同的问题:由于节省了大量时间,我想,我继续使用 pry 手动运行 Nex 和 thetinman 的方法。这是我发现我的电脑挂起的地方,就像我的原始代码一样。所以我继续测试,但使用的是我的原始代码。

Rehearsal ---------------------------------------------
Original    7.980000   0.140000   8.120000 (  8.118340)
Nex         9.460000   0.080000   9.540000 (  9.546889)
Thetinman  10.980000   0.070000  11.050000 ( 11.042459)
----------------------------------- total: 28.710000sec

                user     system      total        real
Original   16.280000   0.140000  16.420000 ( 16.414070)
Nex        15.370000   0.080000  15.450000 ( 15.454174)
Thetinman  20.100000   0.090000  20.190000 ( 20.195533)

我的代码、Nex 和 thetinman 的方法似乎具有可比性,其中 Nex 是使用 Benchmark 最快的。然而,Benchmark 似乎并没有说明全部情况,因为使用 pry 手动测试代码会使所有方法花费的时间大大延长,以至于我在取回结果之前取消了。

我还有一些问题:

  1. 在 IRB/Pry 中运行这样的东西是否有特定的东西会产生这些奇怪的结果,使代码运行速度大大降低?
  2. 如果我运行original_method.countnex_method.countthetinmans_method.count,它们似乎都很快返回。
  3. 由于内存问题和可扩展性,thetinman 和 nex 建议不要使用原始方法。但是,将来有没有办法用基准测试之类的方法来测试内存使用情况?

NEX 更新,使用activerecord-import

@nex,这是你的意思吗?这对我来说似乎仍然很慢,但我不确定你说的是什么意思:

在该块内导入一组数据。

你建议如何修改它?

def parse_line(line)
   filing={
   'name' => line[0..50].strip,
   'form' => line[51..70].strip,
   'type'  => line[71..80].strip,
   'date' => line[81..90].strip,
    'location' => line[91..-1].strip
    }    
end

def import_files
 result=[]
 parse_list_nix(file_path){|line|
    filing=parse_line(line)    
    result.push(Filing.new(filing))

 }
 Filing.import result   #result is an array of new records that are all imported at once
end

activerecord-import如您所见,该方法的结果要慢得多:

Rehearsal ------------------------------------------
import 534.840000   1.860000 536.700000 (553.507644)
------------------------------- total: 536.700000sec

             user     system      total        real
import 263.220000   1.320000 264.540000 (282.751891)

这种缓慢的导入过程是否正常?

它对我来说似乎超级慢。我试图弄清楚如何加快速度,但我没有想法。

4

2 回答 2

2

如果没有样本数据,很难确认这一点,但是,基于原始代码,我可能会写这样的东西:

require 'english'

# Parse the fixed-length fields
def parse_list(file_path,import=false)

  return false unless File.exist?(file_path)

  result=[]
  File.foreach(file_path) do |rs|
    next unless $INPUT_LINE_NUMBER > 5
    next unless rs.length > 140

    r = rs.strip
    if r > '' 
      name, form, type, date, location = r.unpack('A51 A20 A10 A10 A*').map(&:strip)
      result << {
        'name'     => name,
        'form'     => form,
        'type'     => type,
        'date'     => date,
        'location' => location
      }
    end
  end

  result
end

220,000 行并不是我来自的大文件。到上午中旬,我们得到的日志文件是 3 倍,因此使用任何文件 I/O 来破坏文件都是不可行的。Ruby 的 IO 类有两种用于逐行 I/O 的方法和一个返回数组的数字。你想要前者,因为它们是可扩展的。除非您可以保证正在读取的文件能够舒适地放入 Ruby 的内存中,否则请避免使用后者。

于 2013-05-11T00:32:22.650 回答
2

问题是你正在填满你的记忆。你打算怎么处理这个结果?它是否必须作为一个整体留在你的记忆中,或者是否可以选择只用一个块逐行处理它?

此外,您不应在此处使用 readlines。做这样的事情,因为它使用枚举器:

def parse_list(file_path, import=false)
  i = 0
  File.open(file_path,'r').each_line do |line|
    line.strip!
    next if (i+=1) < 5 || line.length < 141
    filing = { 'name' => r[0..50].strip,
               'form' => r[51..70].strip,
               'type'  => r[71..80].strip,
               'date' => r[81..90].strip,
               'location' => r[91..-1].strip }
    yield(filling) if block_given?
  end
end

# and calling it like this:
parse_list('/tmp/foobar'){ |filling|
  Filing.new(filing).import
}
于 2013-05-11T00:37:05.577 回答