Подсчитать количество строк в файле, не считывая весь файл в память? - PullRequest
51 голосов
/ 16 апреля 2010

Я обрабатываю огромные файлы данных (миллионы строк каждый).

Перед тем, как приступить к обработке, я бы хотел подсчитать количество строк в файле, чтобы потом можно было указать, как далеко продвигается обработка.

Из-за размера файлов нецелесообразно считывать весь файл в память, а просто считать, сколько строк. У кого-нибудь есть хорошее предложение, как это сделать?

Ответы [ 14 ]

0 голосов
/ 23 августа 2018

Вы можете прочитать только последнюю строку и увидеть ее номер:

f = File.new('huge-file')
f.readlines[-1]
count = f.lineno
0 голосов
/ 06 апреля 2016

wc -l в Ruby с меньшим объемом памяти, ленивый способ:

(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)

как первоначально показано Shugo Maeda .

Пример:

$ 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
0 голосов
/ 27 июля 2014

Использование foreach без inject примерно на 3% быстрее, чем с inject. И то, и другое намного быстрее (более чем в 100 раз по моему опыту), чем использование getc.

Использование foreach без inject также может быть немного упрощено (относительно фрагмента, приведенного в другом месте в этой теме) следующим образом:

count = 0;  File.foreach(path) { count+=1}
puts "count: #{count}"
0 голосов
/ 30 августа 2013

С текстовыми файлами в стиле UNIX это очень просто

f = File.new("/path/to/whatever")
num_newlines = 0
while (c = f.getc) != nil
  num_newlines += 1 if c == "\n"
end

Вот и все. Для текстовых файлов MS Windows вам придется проверить последовательность "\ r \ n" вместо просто "\ n", но это немного труднее. Для текстовых файлов Mac OS Classic (в отличие от Mac OS X), вы должны проверить "\ r" вместо "\ n".

Итак, да, это похоже на C. И что? С, и Руби офигенно, потому что, когда ответ C проще всего, это то, что вы можете ожидайте, что ваш код Ruby будет выглядеть так. Надеюсь, у тебя нет Я уже хвастался Java.

Кстати, пожалуйста, даже не рассматривайте ни один из ответов выше которые используют метод IO#read или IO#readlines, в свою очередь вызывая Строковый метод на том, что было прочитано. Вы сказали, что не хотите прочитать весь файл в память, и это именно то, что они делают. Вот почему Дональд Кнут рекомендует людям понять, как программировать ближе к оборудованию, потому что если они этого не сделают, они в конечном итоге писать "странный код". Очевидно, вы не хотите кодировать близко к аппаратное обеспечение, когда вам не нужно, но это должно быть здравым смыслом. Однако вы должны научиться распознавать случаи, которые у вас есть чтобы приблизиться к гайкам и болтам, таким как этот.

И не пытайтесь получить больше «объектно-ориентированного», чем ситуация призывает для. Это неловкая ловушка для новичков, которые хотят посмотреть более сложный, чем они есть на самом деле. Ты всегда должен быть рад для тех времен, когда ответ действительно прост, и не будет разочарован, когда нет сложности, чтобы дать вам возможность написать «впечатляющий» код. Однако, если вы хотите выглядеть несколько «объектно-ориентированный» и не прочь прочитать всю строку в памяти за раз (то есть вы знаете, строки достаточно короткие), вы может сделать это

f = File.new("/path/to/whatever")
num_newlines = 0
f.each_line do
  num_newlines += 1
end

Это был бы хороший компромисс, но только если линии не слишком долго в этом случае он может даже работать быстрее, чем мой первый решение.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...