2015-05-27 14 views
5

Szukam tagów id3 w pliku piosenki. Plik może zawierać znaczniki rozszerzone id3v1, id3v1 (znajdujące się na końcu pliku) oraz znaczniki id3v2 (zwykle umieszczone na początku). W przypadku znaczników id3v1 mogę użyć pliku File.read (plik_pliku) i wyciągnąć ostatnie 355 bajtów (128 + 227 dla rozszerzonego znacznika). Jednak w przypadku znaczników id3v2 muszę od początku przeszukać plik, szukając 10 bajtowego wzorca id3v2. Chciałbym uniknąć wielokrotnego otwierania i zamykania tego samego pliku wielokrotnie, gdy szukam różnych znaczników, więc pomyślałem, że najlepiej będzie użyć File.stream! (Song_file) i wysłać strumień plików do różnych funkcji, aby wyszukać różne tagi.Najbardziej efektywny sposób wyszukiwania plików dla wzorów bajtów w 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) Czy zapisuję dowolny plik wykonywalny, tworząc plik File.stream! raz i wysłanie go do różnych funkcji (będę skanował dziesiątki tysięcy plików, więc ważne jest zaoszczędzić trochę czasu)? Czy powinienem po prostu użyć File.read dla tagów id3v1 i File.stream! dla tagów id3v2?

2) pojawia się błąd w linii:

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

ponieważ Stream.take (file_stream, -355) jest funkcją, a nie binarny. Jak mogę przekształcić go w plik binarny, który mogę dopasować do wzorca?

+0

Nie mam dla ciebie odpowiedzi, ale gdybym był tobą, naprawdę unikałbym magicznych liczb. Zakładam, że -355 to suma 128 i 227? Gdybym był tobą, ustawiłbym atrybut modułu o nazwie "@ id_extended_tag_binsize" na 227 i "@ id3_tag_binsize" na 128, a następnie użyłbym tych atrybutów w Stream.take w ten sposób: Stream.take (strumień plików, - ('@ id_extended_tag_binsize' + '@ id3_tag_binsize')) Mniej podatny na błędy i znacznie łatwiejszy do odczytania i zrozumienia. –

+0

Po pierwsze, File.stream musi zostać poinformowany, aby czytać bajty, a nie wiersze, używając składni File.stream! (Path, [: read],: bytes). Następnie nie sądzę, że -355 może być używany z Stream.take, ponieważ strumień nie jest listą, ale potencjalną listą, która musi zostać sfinalizowana za pomocą wywołania Stream.to_list. – GavinBrelstaff

+0

Dzięki za sugestie. Zmieniłem odpowiednio swój kod. –

Odpowiedz

6

Uważam, że Państwa wdrożenie jest niepotrzebnie skomplikowane ze względu na poleganie na strumieniu. Spraw, aby działało, sprawiało, że było piękne, a potem szybko (ale tylko w razie potrzeby).

Dla uproszczenia najpierw wczytałbym wszystko do pamięci. Po prostu użyj File.read!/1. Następnie możesz użyć funkcji z: modułu binarnego do wyszukiwania wzorów (:binary.match/2), podzielić go (:binary.split/2) lub pobrać określoną część (:binary.part/3). Nie trzeba również mieszać plików File.stream i File.read, wystarczy raz przeczytać i przekazać ten sam plik binarny.

Również, bardzo ważne, nie używaj modułu String. Łańcuch jest przeznaczony do pracy z binarkami kodowanymi w UTF-8. Chcesz użyć: modułu binarnego dla wszystkich operacji na poziomie bajtów.

Wreszcie, Stream.take/2 zawsze zwraca funkcje, ponieważ jest leniwy. Zamiast tego chcesz użyć Enum.take/2 (akceptuje strumienie, ponieważ strumienie są również przeliczalne). Chociaż, jak już powiedziałem, całkowicie pominęłbym strumień.

+0

Doceniam odpowiedź. Wezmę twoją radę i użyję File.read!/1. Nie wiedziałem o module String. Pomyślałem, że jeśli bajty są znakami, możesz traktować je jako pliki binarne lub łańcuchowe. –

Powiązane problemy