Как обрабатывать большие двоичные данные в Clojure? - PullRequest
6 голосов
/ 22 августа 2010

Как обрабатывать большие двоичные файлы данных в Clojure?Давайте предположим, что данные / файлы занимают около 50 МБ - достаточно мало для обработки в памяти (но не в простой реализации).

Следующий код корректно удаляет ^ M из небольших файлов, но выдает OutOfMemoryError для больших файлов(например, 6 МБ):

(defn read-bin-file [file]
  (to-byte-array (as-file file)))

(defn remove-cr-from-file [file]
  (let [dirty-bytes (read-bin-file file)
        clean-bytes (filter #(not (= 13 %)) dirty-bytes)
        changed?    (< (count clean-bytes) (alength dirty-bytes))]    ; OutOfMemoryError
    (if changed?
      (write-bin-file file clean-bytes))))    ; writing works fine

Кажется, что байтовые массивы Java нельзя трактовать как seq , поскольку они крайне неэффективны.

С другой стороны,решения с aset, aget и areduce являются раздутыми, безобразными и необходимыми, потому что вы не можете использовать библиотеку последовательностей Clojure.

Чего мне не хватает? Как обрабатывать большие двоичные файлы данных в Clojure?

1 Ответ

6 голосов
/ 22 августа 2010

Я бы, вероятно, лично использовал aget / aset / areduce здесь - они могут быть необходимыми, но они полезны при работе с массивами, и я не нахожу их особенно уродливыми.Если вы хотите обернуть их в красивую функцию, то, конечно, вы можете: -)

Если вы решили использовать последовательности, то ваша проблема будет в построении и обходе seq, так как это потребует созданияи хранение нового объекта seq для каждого байта в массиве.Это, вероятно, ~ 24 байта для каждого байта массива ......

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

Следующее может работать (не проверено), но будет зависеть от того, что write-bin-file реализован дружественным образом:

(defn remove-cr-from-file [file]
  (let [dirty-bytes (read-bin-file file)
        clean-bytes (filter #(not (= 13 %)) dirty-bytes)
        changed-bytes (count (filter #(not (= 13 %)) dirty-bytes))
        changed?    (< changed-bytes (alength dirty-bytes))]   
    (if changed?
      (write-bin-file file clean-bytes))))

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

...