如何基于动态格式从歌曲文件名提取元标签?(Bash/Python)
这问题我之前处理过不少,核心思路是把用户提供的元标签格式反向转换成正则表达式——把格式里的%n、%t这类占位符替换成能匹配对应内容的正则分组,再用这个正则去匹配文件名,就能提取出对应的元数据了。下面分别给出Python和Bash的可行实现:
Python 实现
Python的re模块非常适合做这种字符串匹配提取,我们可以写一个通用函数,把格式字符串转成正则,再提取分组内容:
import re def extract_metadata(filename, format_str): # 定义占位符对应的正则规则,可根据需求调整 placeholder_map = { '%n': r'(\d+)', # 曲目号:匹配数字 '%t': r'([^.]+?)', # 标题:匹配到下一个分隔符/后缀前的内容(非贪婪) '%a': r'([^.]+?)', # 艺术家:同理 '%b': r'([^.]+?)', # 专辑:同理 } # 先转义格式字符串里的正则特殊字符(比如.、-、()等),再替换占位符 escaped_format = re.escape(format_str) for placeholder, regex in placeholder_map.items(): escaped_format = escaped_format.replace(re.escape(placeholder), regex) # 匹配文件名(注意要加上$匹配结尾,避免部分匹配) match = re.match(f"^{escaped_format}$", filename) if not match: return None # 返回提取的元数据字典,键对应占位符去掉% metadata = {} keys = [p[1:] for p in placeholder_map.keys()] for key, value in zip(keys, match.groups()): metadata[key] = value.strip() # 去掉前后空格 return metadata # 示例调用 filename = "03 - Bohemian Rhapsody - Queen - A Night at the Opera.mp3" format_str = "%n - %t - %a - %b.mp3" result = extract_metadata(filename, format_str) print(result) # 输出:{'n': '03', 't': 'Bohemian Rhapsody', 'a': 'Queen', 'b': 'A Night at the Opera'}
注意事项
- 可以根据实际需求调整
placeholder_map里的正则规则,比如如果标题允许包含-,可以把%t的正则改成(.+?)(非贪婪匹配到下一个占位符) - 如果格式字符串里有自定义分隔符(比如
_或者|),只要占位符位置正确,正则会自动适配
Bash 实现
Bash 3.0+支持正则表达式匹配(=~运算符),我们可以用类似的思路,把格式字符串转成正则,再提取匹配结果:
extract_metadata() { local filename="$1" local format_str="$2" # 定义占位符对应的正则分组 declare -A placeholder_map=( ['%n']='([0-9]+)' ['%t']='([^.]+?)' ['%a']='([^.]+?)' ['%b']='([^.]+?)' ) # 转义格式字符串里的特殊字符,再替换占位符 local escaped_format=$(printf '%s' "$format_str" | sed 's/[.[\*^$()+?{|]/\\&/g') for placeholder in "${!placeholder_map[@]}"; do escaped_format=$(printf '%s' "$escaped_format" | sed "s/$(printf '%s' "$placeholder" | sed 's/[\/&]/\\&/g')/${placeholder_map[$placeholder]}/g") done # 执行匹配 if [[ "$filename" =~ ^$escaped_format$ ]]; then # 提取分组,按占位符顺序返回 local i=1 for placeholder in '%n' '%t' '%a' '%b'; do local key="${placeholder:1}" printf "%s=%s\n" "$key" "${BASH_REMATCH[$i]}" ((i++)) done else echo "No match found" >&2 return 1 fi } # 示例调用 filename="03 - Bohemian Rhapsody - Queen - A Night at the Opera.mp3" format_str="%n - %t - %a - %b.mp3" extract_metadata "$filename" "$format_str" # 输出: # n=03 # t=Bohemian Rhapsody # a=Queen # b=A Night at the Opera
注意事项
- Bash的正则匹配默认是贪婪的,所以如果格式里的分隔符和内容可能重叠,需要调整正则规则(比如用
[^-]+来匹配到下一个-之前的内容) - 转义特殊字符的步骤很重要,否则格式里的
.会被当成匹配任意字符,导致错误匹配
内容的提问来源于stack exchange,提问作者onda47
相关产品推荐
相关产品推荐

