You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在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;
}
?>

关键部分解释:

  1. 模式转正则:

    • 先用preg_quote转义模式里的正则特殊字符(比如.、-),避免它们被当作正则语法解析。
    • 把%track%替换成(\d+),匹配任意数字序列的曲目号;%artist%和%title%替换成(.*?),非贪婪匹配任意字符(这样不会把分隔符也包含进去)。
    • 最后添加^(字符串开头)和\.\w+$(匹配任意文件扩展名,比如.mp3、.flac),并开启i修饰符忽略大小写。
  2. 捕获组提取:

    • 通过拆分原模式,找到%artist%和%title%在模式中的顺序,从而确定它们在$matches数组中的索引(因为$matches[0]是整个匹配的字符串,捕获组从$matches[1]开始)。
    • 用trim去掉提取内容前后的多余空格,保证结果整洁。
  3. 匹配优先级:

    • 因为是按SEARCHPATTERNS的顺序逐个匹配,所以把更精确的模式(比如包含track、artist、title的)放在前面,确保优先匹配更完整的规则。

这样运行你的测试用例,每个文件名都能正确提取出对应的艺术家和标题啦。

内容的提问来源于stack exchange,提问作者RWC

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.07 22:47:42