如何在PHP中通过多模式提取字符串?正则实现方案求助
解决从音频文件名提取艺术家和标题的问题
嗨,我完全理解你现在因为新冠影响思路不清的状态,别着急,我们一步步来搞定这个问题。你的需求是模仿MP3tag的逻辑,通过预定义的模式从文件名里提取艺术家和标题,核心就是把那些模式转换成正则表达式,然后匹配提取对应内容。
首先,先修正你代码里的一个小错误:你定义了UNKNOWN_ARTIST_TITLE_ARRAY常量,但函数里误用了UNKNOWN_ARTIST_TITLE_STRINGS,我们先统一成前者。
接下来,核心思路是把你定义的每个SEARCHPATTERNS模式转换成对应的正则表达式,然后逐个匹配文件名,一旦匹配成功就提取对应的艺术家和标题。下面是完整的实现代码:
<?php define('SEARCHPATTERNS', array( '%track%. %artist% - %title%', '%track% - %artist% - %title%', '%track%. %title%', '%track% - %title%', '%title%')); define('UNKNOWN_ARTIST_TITLE_ARRAY', array('?', '?')); $fileNames = array( '0780. Janis Joplin - Mercedes Benz.mp3', '0780. Janis Joplin - Mercedes Benz.flac', '0780 - Janis Joplin - Mercedes Benz.mp3', '0780 - Janis Joplin - Mercedes Benz.flac', '0780. Mercedes Benz.mp3', '0780. Mercedes Benz.flac', '0780 - Mercedes Benz.mp3', '0780 - Mercedes Benz.flac', 'Mercedes Benz.mp3', 'Mercedes Benz.flac', ); // Test some file names foreach($fileNames as $fileName) { $titleAndArtist = GetTitleArtistUsingSearchPattern($fileName); var_dump($titleAndArtist); } function GetTitleArtistUsingSearchPattern($fileName) { foreach(SEARCHPATTERNS as $pattern) { // 把自定义模式转换成正则表达式 // 1. 先转义模式里的正则特殊字符 $regexPattern = preg_quote($pattern, '/'); // 2. 替换占位符为对应的捕获组 $regexPattern = str_replace('%track%', '(\d+)', $regexPattern); $regexPattern = str_replace('%artist%', '(.*?)', $regexPattern); $regexPattern = str_replace('%title%', '(.*?)', $regexPattern); // 3. 添加开头锚定、结尾匹配扩展名的规则,忽略大小写 $fullRegex = '/^' . $regexPattern . '\.\w+$/i'; // 执行匹配 if(preg_match($fullRegex, $fileName, $matches)) { $artist = ''; $title = ''; // 根据原模式判断捕获组对应的内容 if(strpos($pattern, '%artist%') !== false) { // 模式包含艺术家,找到对应的捕获组位置 $parts = explode('%', $pattern); $artistIndex = 0; foreach($parts as $part) { if($part === 'artist') break; if(in_array($part, ['track', 'artist', 'title'])) $artistIndex++; } $artist = trim($matches[$artistIndex + 1]); // $matches[0]是整个匹配串 } // 提取标题 if(strpos($pattern, '%title%') !== false) { $parts = explode('%', $pattern); $titleIndex = 0; foreach($parts as $part) { if($part === 'title') break; if(in_array($part, ['track', 'artist', 'title'])) $titleIndex++; } $title = trim($matches[$titleIndex + 1]); } // 返回结果,空值用默认值替换 return array( empty($artist) ? UNKNOWN_ARTIST_TITLE_ARRAY[0] : $artist, empty($title) ? UNKNOWN_ARTIST_TITLE_ARRAY[1] : $title ); } } return UNKNOWN_ARTIST_TITLE_ARRAY; } ?>
关键部分解释:
模式转正则:
- 先用
preg_quote转义模式里的正则特殊字符(比如.、-),避免它们被当作正则语法解析。 - 把
%track%替换成(\d+),匹配任意数字序列的曲目号;%artist%和%title%替换成(.*?),非贪婪匹配任意字符(这样不会把分隔符也包含进去)。 - 最后添加
^(字符串开头)和\.\w+$(匹配任意文件扩展名,比如.mp3、.flac),并开启i修饰符忽略大小写。
- 先用
捕获组提取:
- 通过拆分原模式,找到
%artist%和%title%在模式中的顺序,从而确定它们在$matches数组中的索引(因为$matches[0]是整个匹配的字符串,捕获组从$matches[1]开始)。 - 用
trim去掉提取内容前后的多余空格,保证结果整洁。
- 通过拆分原模式,找到
匹配优先级:
- 因为是按
SEARCHPATTERNS的顺序逐个匹配,所以把更精确的模式(比如包含track、artist、title的)放在前面,确保优先匹配更完整的规则。
- 因为是按
这样运行你的测试用例,每个文件名都能正确提取出对应的艺术家和标题啦。
内容的提问来源于stack exchange,提问作者RWC
相关产品推荐
相关产品推荐

