2014-01-21 4 views
2

В настоящее время я пытаюсь выяснить, как перечислить архив TAR (gzipped) в Haskell. Codec.Archive.Tar кажется правильным выбором для задачи, но я не могу понять, как mapentryPath над Entries моноидом.Список TAR-архивов в Haskell

Предположим, что TAR содержит записи (только файлы) a.txt, b.txt, c.txt и имеет имя foo.tar.gz. Вот мой код, чтобы прочитать файл:

import qualified Codec.Archive.Tar as Tar 
import qualified Data.ByteString.Lazy as BS 
import qualified Codec.Compression.GZip as GZip 

foldEntryToPath :: Tar.Entry -> [String] -> [String] 
foldEntryToPath entry list = list ++ [show $ Tar.entryPath entry] 

-- Converts TAR errors to a string. 
entryFailMapper :: String -> [String] 
entryFailMapper err = [err] 

main = do 
     fileContent <- fmap GZip.decompress $ BS.readFile "foo.tar.gz" 
     entries <- fmap Tar.read fileContent :: Tar.Entries 
     -- Here I don't know how to correctly apply fmap 
     entryPaths <- Tar.foldEntries foldEntryToPath [] entryFailMapper entries :: [String] 
     -- This should print ["a.txt", "b.txt", "c.txt"] 
     print entryPaths 

Вот ошибка печатается runghc:

readtar.hs:14:49: 
Expecting one more argument to `Tar.Entries' 
In an expression type signature: Tar.Entries 
In a stmt of a 'do' block: 
    entries <- fmap Tar.read fileContent :: Tar.Entries 
In the expression: 
    do { fileContent <- fmap GZip.decompress 
         $ BS.readFile "foo.tar.gz"; 
     entries <- fmap Tar.read fileContent :: Tar.Entries; 
     entryPaths <- Tar.foldEntries 
         foldEntryToPath [] (\ x -> [...]) entries :: 
         [String]; 
     print entryPaths } 

До сих пор у меня мало знаний о Haskell, но, прочитав the docs я не знаю, почему Tar.Entries является typeclass (это правильный термин, когда он говорит expecting n more arguments to <type>?) или какой правильный тип использовать.

Любая помощь будет оценена!

+1

к сведению, что это не последняя версия Lib. – Vektorweg

+0

@Vektorweg Спасибо, я не заметил, что (я обновил ссылки!). Однако это было просто ссылки doc (нашли их с помощью Google в первую очередь), я установил lib, используя 'cabal install tar', который фактически установил 0.4.0.1 –

ответ

1

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

Одной из основных проблем было foldr -подобное поведение Tar.foldEntries. В действительности у меня есть файл TAR размером ~ 25 ГБ, содержащий несколько миллионов записей. См. the HaskellWiki для получения информации о том, почему это плохая идея. (Примечание:. Будучи эффективным не был вопросом, но я думаю, что foldEntries -бесплатно решение лучше для этого конкретного USECASE

Поэтому я написал свою собственную рекурсивную функцию Tar.Entries -> [String] отображения Даже если ошибки в настоящее время не особенно хорошо обработаны. , она должна обеспечивать хорошую отправную точку.

import qualified Codec.Archive.Tar as Tar 
import qualified Data.ByteString.Lazy as BS 
import qualified Codec.Compression.GZip as GZip 

entriesToPaths :: Tar.Entries Tar.FormatError -> [String] 
entriesToPaths (Tar.Next entry entries) = [Tar.entryPath entry] ++ entriesToPaths entries 
entriesToPaths Tar.Done = [] :: [String] 
entriesToPaths (Tar.Fail e) = ["Error"] 

main = do 
     fileContent <- fmap GZip.decompress $ BS.readFile "foo.tar.gz" 
     let entries = Tar.read fileContent 
     let entryPaths = entriesToPaths entries 
     -- This should print ["a.txt", "b.txt", "c.txt"] 
     print entryPaths 
+0

В этом случае я не получаю ваше отрицательное замечание о foldr, так как ваша функция entriesToPaths - это просто рукописный склад. – kosmikus

+0

@kosmikus Я думаю, что вы правы. Первоначально я думал, что я реализовал foldl, но это не так. Однако решение 'foldEntries' все еще искажало мой компьютер, в то время как это не с моей собственной реализацией. Завтра я снова проверю ваше решение, возможно, проблемы не вызваны самим 'foldEntries', а другим заявлением. –

+0

Ну, в вашем вопросе, в 'foldEntryToPath', вы добавляете новый элемент в * конец * списка. Это никогда не будет эффективным.В этом решении в 'entryToPaths' вы добавляете новую запись в начало. Поэтому это различие является важным. Не используйте ли вы 'foldEntries' или нет. – kosmikus

1

Я думаю foldEntryToPath должно быть исправлено:

foldEntryToPath :: Tar.Entry -> [String] -> [String] 
foldEntryToPath entry list = (show $ Tar.entryPath entry) : list 

И в main:

fileContent <- fmap GZip.decompress $ BS.readFile "foo.tar.gz" 
let entries = Tar.read fileContent 
let entryPaths = Tar.foldEntries foldEntryToPath [] entryFailMapper entries 
print entryPaths 
+0

Спасибо за ваши усилия! wn, работающий всего за несколько секунд до вашего сообщения. Насколько я понимаю, с вашими изменениями код работает нормально, однако в некоторых случаях поведение 'foldEntries'' foldr 'имеет некоторые серьезные проблемы с производительностью (однако я специально не просил повысить производительность и/или эффективность памяти , поэтому ваш пост по-прежнему верен). См. Мой ответ для альтернативного решения с заменой 'foldEntries'. –

+0

Когда я применяю ваши изменения к коду из вопроса, я получаю эту ошибку в строке 15: 'Не удалось сопоставить тип' Tar.FormatError 'с' [Char] ''. Можете ли вы воспроизвести эту проблему? –

Смежные вопросы