2

当我将文本文件读入内存时,由于新行,它会将我的文本带入末尾并带有 '\n' 。

["Hello\n", "my\n", "name\n", "is\n", "John\n"] 

这是我阅读文本文件的方式

array = File.readlines('text_file.txt')

我需要对这个文本数组进行大量处理,所以我想知道是否应该在第一次创建数组时删除“\n”,或者当我使用正则表达式对每个元素进行处理时,性能明智。

我写了一些(诚然不好的)测试代码来删除“\ n”

array = []
File.open('text_file.txt', "r").each_line do |line|
    data = line.split(/\n/)
    array << data
end
array.flatten!

如果我在第一次创建数组时应该删除“\n”,有没有更好的方法来做到这一点?

如果我想将文件读入 Set 而不是(为了性能),是否有类似于 readlines 的方法来做到这一点?

4

5 回答 5

4

我会使用String#chomp

lines = open('text_file.txt').lines.map(&:chomp)
于 2012-09-13T18:35:00.247 回答
4

您需要运行基准测试,使用 Ruby 的内置基准来确定您最快的选择。

但是,根据经验,我发现“啜饮”文件,即一次读取所有文件,并不比使用带有IO.foreachorFile.foreach的循环快。这是因为 Ruby 和底层操作系统会在读取发生时进行文件缓冲,从而允许您的循环从内存发生,而不是直接从磁盘发生。foreach不会像那样为您剥离行终止符split,因此您需要添加 achomp或者chomp!如果您想改变读入的行:

File.foreach('/path/to/file') do |li|
  puts li.chomp
end

或者

File.foreach('/path/to/file') do |li|
  li.chomp!
  puts li
end

此外,slurping 存在不可扩展的问题。您最终可能会尝试读取比内存更大的文件,从而使您的机器陷入瘫痪,而逐行读取则永远不会这样做。


以下是一些性能数据:

#!/usr/bin/env ruby

require 'benchmark'
require 'fileutils'

FILENAME = 'test.txt'
LOOPS = 1

puts "Ruby Version: #{RUBY_VERSION}"
puts "Filesize being read: #{File.size(FILENAME)}"
puts "Lines in file: #{`wc -l #{FILENAME}`.split.first}"

Benchmark.bm(20) do |x|
  x.report('read.split')           { LOOPS.times { File.read(FILENAME).split("\n") }}
  x.report('read.lines.chomp')     { LOOPS.times { File.read(FILENAME).lines.map(&:chomp) }}
  x.report('readlines.map.chomp1') { LOOPS.times { File.readlines(FILENAME).map(&:chomp) }}
  x.report('readlines.map.chomp2') { LOOPS.times { File.readlines(FILENAME).map{ |s| s.chomp } }}
  x.report('foreach.map.chomp1')   { LOOPS.times { File.foreach(FILENAME).map(&:chomp) }}
  x.report('foreach.map.chomp2')   { LOOPS.times { File.foreach(FILENAME).map{ |s| s.chomp } }}
end

结果:

Ruby Version: 1.9.3
Filesize being read: 42026131
Lines in file: 465440
                           user     system      total        real
read.split             0.150000   0.060000   0.210000 (  0.213365)
read.lines.chomp       0.470000   0.070000   0.540000 (  0.541266)
readlines.map.chomp1   0.450000   0.090000   0.540000 (  0.535465)
readlines.map.chomp2   0.550000   0.060000   0.610000 (  0.616674)
foreach.map.chomp1     0.580000   0.060000   0.640000 (  0.641563)
foreach.map.chomp2     0.620000   0.050000   0.670000 (  0.662912)

在今天的机器上,一个 42MB 的文件可以很安全地读入 RAM。我看到的文件比我们的一些生产主机的内存大得多。虽然foreach速度较慢,但​​如果没有足够的内存,它也不会通过吸收所有内存来让机器瘫痪。

在 Ruby 1.9.3 上,使用该map(&:chomp)方法而不是旧形式的map { |s| s.chomp },要快得多。旧版本的 Ruby 并非如此,因此请谨慎购买。

另外,请注意,在我几年前的 Mac Pro 上,上述所有操作都在不到一秒的时间内处理了数据。总而言之,我会说担心加载速度是过早的优化,真正的问题是加载数据后要做什么。

于 2012-09-13T18:29:47.780 回答
0

如果您想摆脱结束换行符,您可以使用String#chompString#rstrip。我的首选方法是 chomp。

因此,您可以轻松地执行以下操作:

lines.map! { |line| line.chomp }
# or
lines.map! { |line| line.rstrip }
于 2012-09-13T18:35:08.297 回答
0
mvelez@argo:~$ cat test.txt
Hello
my
name
is
John

一个班轮:

arr = File.open("test.txt",'r').read.split

分解这个irb

irb(main):002:0> f = File.open("test.txt",'r')
=> #<File:test.txt>
irb(main):003:0> file_contents = f.read
=> "Hello\nmy\nname\nis\nJohn\n\n"
irb(main):004:0> file_contents.split
=> ["Hello", "my", "name", "is", "John"]
于 2012-09-13T18:39:03.147 回答
0

在这些情况下,我更喜欢使用 strip 而不是 split ,并在第一次处理线路后立即执行此操作。在 readline 之后使用 split 是过度杀伤 imo。所以代码片段将是

array = []
File.open('text_file.txt', "r").each_line do |line|
    array << data.strip
end
于 2012-09-13T18:33:55.737 回答