我在歌曲文件中搜索id3标签。一个文件可以有id3v1,id3v1扩展标签(位于文件末尾)以及id3v2标签(通常位于开头)。对于id3v1标签,我可以使用File.read(song_file)并拉出最后355个字节(扩展标签为128 + 227)。但是,对于id3v2标签,我需要从头开始搜索文件,寻找10字节的id3v2模式。我想避免在搜索不同标签时反复打开和关闭同一文件的任何开销,所以我认为最好的方法是使用File.stream!(song_file)并将文件流发送到不同的函数来搜索不同的标签。
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)我是否通过创建File.stream来保存任何运行时!一次并将其发送到不同的功能(我将扫描成千上万的文件,因此节省一点时间很重要)?或者我应该只使用File.read作为id3v1标签和File.stream!对于id3v2标签?
2)我在行中收到错误:
<< id3_extended_tag :: binary-size(227), id3_tag :: binary-size(128) >> = Stream.take(file_stream, -355)
因为Stream.take(file_stream,-355)是一个函数,而不是二进制文件。如何将其转换为可以模式匹配的二进制文件?
答案 0 :(得分:6)
我认为由于对流的依赖,您的实施不必要地复杂化。让它工作,使它漂亮,然后快速(但只在必要时)。
为简单起见,我首先将所有内容加载到内存中。只需使用File.read!/1
即可。然后,您可以使用以下函数:binary module来搜索模式(:binary.match/2
),拆分它(:binary.split/2
)或抓取某个部分(:binary.part/3
)。也没有必要混合File.stream和File.read,只需读取一次并传递相同的二进制文件。
另外,非常重要的是,不要使用String模块。字符串用于处理UTF-8编码的二进制文件。您希望将:binary模块用于所有字节级操作。
最后,Stream.take/2
总是返回函数,因为它是惰性的。您想要使用Enum.take/2
(它接受流,因为流也是可枚举的)。虽然,正如我所说,我会完全跳过这些流。