【发布时间】:2015-08-09 11:50:57
【问题描述】:
我正在歌曲文件中搜索 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) 是一个函数,而不是二进制文件。如何将其转换为可以进行模式匹配的二进制文件?
【问题讨论】:
-
我没有答案,但如果我是你,我真的会避免使用幻数。我假设 -355 是 128 和 227 的总和?如果我是你,我会将名为
@id_extended_tag_binsize的模块属性设置为 227,将@id3_tag_binsize设置为 128,然后在 Stream.take 中使用这些属性,如下所示: Stream.take(file_stream, -(@id_extended_tag_binsize+ @987654326 @)) 不易出错,更容易阅读和理解。 -
首先,需要告知 File.stream 使用语法 File.stream!(path, [:read ], :bytes) 读取字节而不是行。接下来,我认为 -355 不能与 Stream.take 一起使用,因为 stream 不是列表,而是需要通过 Stream.to_list 调用来完成的潜在列表。
-
感谢您的建议。我已经相应地更改了我的代码。