전체 파일을 메모리로 읽지 않고 파일의 줄 수를 계산합니까?
거대한 데이터 파일 (각각 수백만 줄)을 처리하고 있습니다.
처리를 시작하기 전에 파일의 줄 수를 확인하여 처리가 얼마나 진행되고 있는지 표시 할 수 있습니다.
파일의 크기 때문에 전체 파일을 메모리로 읽는 것은 실용적이지 않습니다. 단지 몇 줄이 있는지 계산하는 것뿐입니다. 누구든지 이것을 수행하는 방법에 대한 좋은 제안이 있습니까?
Unix 환경에 있다면 wc -l
작업을 맡길 수 있습니다.
전체 파일을 메모리로로드하지 않습니다. 스트리밍 파일 및 단어 / 줄 수에 최적화되어 있기 때문에 Ruby에서 직접 파일을 스트리밍하는 것보다 성능이 충분합니다.
SSCCE :
filename = 'a_file/somewhere.txt'
line_count = `wc -l "#{filename}"`.strip.split(' ')[0].to_i
p line_count
또는 명령 줄에 전달 된 파일 모음을 원하는 경우 :
wc_output = `wc -l "#{ARGV.join('" "')}"`
line_count = wc_output.match(/^ *([0-9]+) +total$/).captures[0].to_i
p line_count
한 번에 한 줄씩 파일 읽기 :
count = File.foreach(filename).inject(0) {|c, line| c+1}
또는 Perl-ish
File.foreach(filename) {}
count = $.
또는
count = 0
File.open(filename) {|f| count = f.read.count("\n")}
보다 느릴 것입니다
count = %x{wc -l #{filename}}.split.first.to_i
어떤 언어를 사용하든 상관 없습니다. 줄 길이가 가변적이라면 전체 파일을 읽어야합니다. 그 이유는 줄 바꿈이 어디에나있을 수 있고 파일을 읽지 않고는 알 수있는 방법이 없기 때문입니다 (캐시되지 않는다고 가정하면 일반적으로 그렇지 않습니다).
진행 상황을 표시하려면 두 가지 현실적인 옵션이 있습니다. 가정 된 줄 길이를 기반으로 진행 상황을 추정 할 수 있습니다.
assumed lines in file = size of file / assumed line size
progress = lines processed / assumed lines in file * 100%
파일의 크기를 알고 있기 때문입니다. 또는 다음과 같이 진행 상황을 측정 할 수 있습니다.
progress = bytes processed / size of file * 100%
이것으로 충분합니다.
루비 사용 :
file=File.open("path-to-file","r")
file.readlines.size
325.477 라인 파일에서 wc -l보다 39 밀리 초 더 빠름
게시 된 솔루션 요약
require 'benchmark'
require 'csv'
filename = "name.csv"
Benchmark.bm do |x|
x.report { `wc -l < #{filename}`.to_i }
x.report { File.open(filename).inject(0) { |c, line| c + 1 } }
x.report { File.foreach(filename).inject(0) {|c, line| c+1} }
x.report { File.read(filename).scan(/\n/).count }
x.report { CSV.open(filename, "r").readlines.count }
end
807802 줄이있는 파일 :
user system total real
0.000000 0.000000 0.010000 ( 0.030606)
0.370000 0.050000 0.420000 ( 0.412472)
0.360000 0.010000 0.370000 ( 0.374642)
0.290000 0.020000 0.310000 ( 0.315488)
3.190000 0.060000 3.250000 ( 3.245171)
DJ의 답변과 동일하지만 실제 Ruby 코드를 제공합니다.
count = %x{wc -l file_path}.split[0].to_i
첫 번째 부분
wc -l file_path
당신에게 준다
num_lines file_path
split
와 to_i
숫자에 그것을 넣어.
내가 완전히 이해하지 못하는 이유로 파일을 사용하여 줄 바꿈을 스캔하는 File
것이 CSV#readlines.count
.
다음 벤치 마크에서는 1,045,574 줄의 데이터와 4 개의 열이있는 CSV 파일을 사용했습니다.
user system total real
0.639000 0.047000 0.686000 ( 0.682000)
17.067000 0.171000 17.238000 ( 17.221173)
벤치 마크 코드는 다음과 같습니다.
require 'benchmark'
require 'csv'
file = "1-25-2013 DATA.csv"
Benchmark.bm do |x|
x.report { File.read(file).scan(/\n/).count }
x.report { CSV.open(file, "r").readlines.count }
end
보시다시피 파일에서 줄 바꿈을 검색하는 것이 훨씬 더 빠릅니다.
이 라이너가 하나 있습니다.
puts File.foreach('myfile.txt').count
파일이 CSV 파일 인 경우 파일 내용이 숫자 인 경우 레코드 길이는 매우 균일해야합니다. 파일의 크기를 레코드 길이나 처음 100 개 레코드의 평균으로 나누는 것이 합리적이지 않을까요?
135,000 개 이상의 라인에 대한 테스트 결과가 아래에 나와 있습니다. 이것은 내 벤치 마크 코드입니다.
file_name = '100m.csv'
Benchmark.bm do |x|
x.report { File.new(file_name).readlines.size }
x.report { `wc -l "#{file_name}"`.strip.split(' ')[0].to_i }
x.report { File.read(file_name).scan(/\n/).count }
end
결과는
user system total real
0.100000 0.040000 0.140000 ( 0.143636)
0.000000 0.000000 0.090000 ( 0.093293)
0.380000 0.060000 0.440000 ( 0.464925)
The wc -l
code has one problem. If there is only one line in the file and the last character does not end with \n
, then count is zero.
So, I recommend calling wc when you count more then one line.
With UNIX style text files, it's very simple
f = File.new("/path/to/whatever")
num_newlines = 0
while (c = f.getc) != nil
num_newlines += 1 if c == "\n"
end
That's it. For MS Windows text files, you'll have to check for a sequence of "\r\n" instead of just "\n", but that's not much more difficult. For Mac OS Classic text files (as opposed to Mac OS X), you would check for "\r" instead of "\n".
So, yeah, this looks like C. So what? C's awesome and Ruby is awesome because when a C answer is easiest that's what you can expect your Ruby code to look like. Hopefully your dain hasn't already been bramaged by Java.
By the way, please don't even consider any of the answers above that use the IO#read
or IO#readlines
method in turn calling a String method on what's been read. You said you didn't want to read the whole file into memory and that's exactly what these do. This is why Donald Knuth recommends people understand how to program closer to the hardware because if they don't they'll end up writing "weird code". Obviously you don't want to code close to the hardware whenever you don't have to, but that should be common sense. However you should learn to recognize the instances which you do have to get closer to the nuts and bolts such as this one.
And don't try to get more "object oriented" than the situation calls for. That's an embarrassing trap for newbies who want to look more sophisticated than they really are. You should always be glad for the times when the answer really is simple, and not be disappointed when there's no complexity to give you the opportunity to write "impressive" code. However if you want to look somewhat "object oriented" and don't mind reading an entire line into memory at a time (i.e., you know the lines are short enough), you can do this
f = File.new("/path/to/whatever")
num_newlines = 0
f.each_line do
num_newlines += 1
end
This would be a good compromise but only if the lines aren't too long in which case it might even run more quickly than my first solution.
wc -l
in Ruby with less memory, the lazy way:
(ARGV.length == 0 ?
[["", STDIN]] :
ARGV.lazy.map { |file_name|
[file_name, File.open(file_name)]
})
.map { |file_name, file|
"%8d %s\n" % [*file
.each_line
.lazy
.map { |line| 1 }
.reduce(:+), file_name]
}
.each(&:display)
as originally shown by Shugo Maeda.
Example:
$ curl -s -o wc.rb -L https://git.io/vVrQi
$ chmod u+x wc.rb
$ ./wc.rb huge_data_file.csv
43217291 huge_data_file.csv
Using foreach
without inject
is about 3% faster than with inject
. Both are very much faster (more than 100x in my experience) than using getc
.
Using foreach
without inject
can also be slightly simplified (relative to the snippet given elsewhere in this thread) as follows:
count = 0; File.foreach(path) { count+=1}
puts "count: #{count}"
You can read the last line only and see its number:
f = File.new('huge-file')
f.readlines[-1]
count = f.lineno
ReferenceURL : https://stackoverflow.com/questions/2650517/count-the-number-of-lines-in-a-file-without-reading-entire-file-into-memory
'programing' 카테고리의 다른 글
오전 / 오후 형식으로 시간을 얻는 방법 iphone NSDateFormatter? (0) | 2021.01.16 |
---|---|
Swift에서 두 버전 문자열 비교 (0) | 2021.01.16 |
Android에서보기를 비트 맵으로 변환 (0) | 2021.01.16 |
사이트에 사진을 업로드하고 저장하는 가장 좋은 방법은 무엇입니까? (0) | 2021.01.16 |
openssl을 요구하거나 OpenSSL을 설치하고 ruby (권장)를 다시 빌드하거나 HTTPS가 아닌 소스를 사용할 수 없습니다. (0) | 2021.01.16 |