2015-05-27 16 views
5

Sto cercando i tag id3 in un file di song. Un file può avere tag id3v1, id3v1 estesi (situati alla fine del file) e tag id3v2 (solitamente situati all'inizio). Per i tag id3v1, posso usare File.read (song_file) ed estrarre gli ultimi 355 byte (128 + 227 per il tag esteso). Tuttavia, per i tag id3v2, ho bisogno di cercare il file dall'inizio cercando un pattern id3v2 da 10 byte. Voglio evitare qualsiasi overhead dall'apertura e chiusura dello stesso file ripetutamente mentre cerco i vari tag, quindi ho pensato che il modo migliore sarebbe stato usare File.stream! (File_file) e inviare il flusso di file a diverse funzioni per cercare i diversi tag.Il modo più efficace per cercare file per i pattern di byte in Elixir

def parse(file_name) do 
    file_stream = File.stream!(file_name, [], 1) 
    id3v1_tags(file_stream) 
    |> add_tags(id3v2_tags(file_stream)) 
end 

def id3v1_tags(file_stream) do 
    tags = Tags%{} #struct containing desired tags 
    << id3_extended_tag :: binary-size(227), id3_tag :: binary-size(128) >> = Stream.take(file_stream, -355) 
    id3_tag = to_string(id3_tag) 
    if String.slice(id3_tag,0, 3) == "TAG" do 
    Map.put(tags, :title, String.slice(id3_tag, 3, 30)) 
    Map.put(tags, :track_artist, String.slice(id3_tag, 33, 30)) 
    ... 
    end 
    if String.slice(id3_extended_tag, 0, 4) == "TAG+" do 
    Map.put(tags, :title, tags.title <> String.slice(id3_extended_tag, 4, 60)) 
    Map.put(tags, :track_artist, tags.track_artist <> String.slice(id3_extended_tag, 64, 60)) 
    ... 
    end 
end 

def id3v2_tags(file_stream) do 
    search for pattern: 
    <<0x49, 0x44, 0x33, version1, version2, flags, size1, size2, size3, size4>> 
end 

1) Sto salvando qualsiasi runtime creando il file.stream! una volta e inviandolo alle diverse funzioni (eseguirò la scansione di decine di migliaia di file, quindi è importante risparmiare un po 'di tempo)? O dovrei semplicemente usare File.read per i tag id3v1 e File.stream! per i tag id3v2?

2) ottengo un errore nella riga:

<< id3_extended_tag :: binary-size(227), id3_tag :: binary-size(128) >> = Stream.take(file_stream, -355) 

perché Stream.take (file_stream, -355) è una funzione, non un binario. Come faccio a trasformarlo in un file binario che posso associare?

+0

Non ho una risposta per te ma se fossi in te, eviterei davvero i numeri magici. Sto assumendo che il -355 sia la somma di 128 e 227? Se fossi in te, imposterei un attributo di modulo chiamato "@ id_extended_tag_binsize" a 227 e "@ id3_tag_binsize" a 128 e quindi userei quegli attributi nello Stream.take in questo modo: Stream.take (file_stream, - ('@ id_extended_tag_binsize' + '@ id3_tag_binsize')) Meno errori inclini e molto più facili da leggere e capire. –

+0

Prima di tutto, File.stream deve essere detto di leggere byte anziché righe usando la sintassi File.stream! (Path, [: read],: bytes). Quindi non penso che il -355 possa essere usato con Stream.take dal momento che stream non è una lista ma una lista potenziale che deve essere finalizzata con una chiamata Stream.to_list. – GavinBrelstaff

+0

Grazie per i suggerimenti. Ho cambiato il mio codice di conseguenza. –

risposta

6

Credo che la vostra implementazione sia inutilmente complessa a causa della dipendenza dal flusso. Falla funzionare, rendila carina, quindi fallo velocemente (ma solo se necessario).

Per semplicità, vorrei prima caricare tutto in memoria. Basta usare File.read!/1. Quindi è possibile utilizzare le funzioni in: modulo binario per cercare modelli (:binary.match/2), dividerlo (:binary.split/2) o afferrare una determinata parte (:binary.part/3). Non c'è bisogno di mescolare anche File.stream e File.read, basta leggerlo una volta e passare lo stesso binario in giro.

Inoltre, molto importante, non utilizzare il modulo String. String ha lo scopo di lavorare con binari codificati in UTF-8. Si desidera utilizzare il: modulo binario per tutte le operazioni a livello di byte.

Infine, Stream.take/2 restituisce sempre le funzioni poiché è pigro. Si desidera invece utilizzare Enum.take/2 (accetta i flussi in quanto gli stream sono anche enumerabili). Sebbene, come ho detto, avrei saltato del tutto la roba del flusso.

+0

Apprezzo la risposta. Prenderò il tuo consiglio e userò File.read!/1. Non lo sapevo del modulo String. Ho pensato che se i byte fossero caratteri, potresti trattarli come binari o stringhe. –

Problemi correlati