php - 使用 PHP 从选项卡中提取和弦

标签 php regex preg-match-all flags

我正在努力弄清楚如何使用 preg_match_all 和 PREG_OFFSET_CAPTURE 解析音乐(文本)标签。

Example输入:

[D#] [G#] [Fm] 
[C#] [Fm] [C#] [Fm] [C#] [Fm] 

[C]La la la la la la [Fm]la la la la [D#]

[Fm]I made this song Cause I [Bbm]love you 
[C]I made this song just for [Fm]you [D#]
[Fm]I made this song deep in [Bbm]my heart

我想要得到的输出:

D# G# Fm 
C# Fm C# Fm C# Fm 

C                 Fm          D#
La la la la la la la la la la

Fm                       Bbm     
I made this song Cause I love you 

C                     Fm  D#
I made this song just for you 

Fm                       Bbm
I made this song deep in my heart

最后,我想用 html 标签包裹和弦。

请注意,和弦之间的空格应与原始输入中这些和弦的位置完全匹配。

我开始逐行解析输入,检测和弦,获取它们的位置,......但我的代码不起作用...... 我的函数 line_extract_chords 有问题,它无法正常工作。

有什么想法吗?

<style>
body{
        font-family: monospace;
        white-space: pre;
</style>

<?php 

function parse_song($content){
    $lines = explode(PHP_EOL, $content); //explode lines

    foreach($lines as $key=>$line){
        $chords_line = line_extract_chords($line);
        $lines[$key] = implode("\n\r",(array)$chords_line);
    }

    return implode("\n\r",$lines);
}

function line_extract_chords($line){

    $line_chords = null; //text line with chords, used to compute offsets
    $line_chords_html = null; //line with chords links
    $found_chords = array();

    $line = html_entity_decode($line); //remove special characters (would make offset problems)

    preg_match_all("/\[([^\]]*)\]/", $line, $matches, PREG_OFFSET_CAPTURE);

    $chord_matches = array();

    if ( $matches[1] ){
        foreach($matches[1] as $key=>$chord_match){

            $chord = $chord_match[0];


            $position = $chord_match[1];
            $offset= $position;
            $offset-= 1; //left bracket
            $offset-=strlen($line_chords); //already filled line

            //previous matches
            if ($found_chords){
                $offset -= strlen(implode('',$found_chords));
                $offset -= 2*(count($found_chords)); //brackets for previous chords
            }

            $chord_html = '<a href="#">'.$chord.'</a>';

            //add spaces
            if ($offset>0){
                $line_chords.= str_repeat(" ", $offset);
                $line_chords_html.= str_repeat(" ", $offset);
            }

            $line_chords.=$chord;
            $line_chords_html.=$chord_html;
            $found_chords[] = $chord;

        }

    }

    $line = htmlentities($line); //revert html_entity_decode()

    if ($line_chords){
        $line = preg_replace('/\[([^\]]*)\]/', '', $line);
        return array($line_chords_html,$line);
    }else{
        return $line;
    }

}
?>

最佳答案

我想提出一个更简单的方法。 它基于这样的假设,即输入数据实际上与您在此处描述的一样一般可解析。

<style>
.line{
    font-family: monospace;
    white-space: pre;
    margin-bottom:0.75rem;
}

.group{
    display: inline-block;
    margin-right: 0.5rem;
}
.group .top,
.group .top{
    display: block;
}
</style>
<?php

$input = "[D#] [G#] [Fm] 
[C#] [Fm] [C#] [Fm] [C#] [Fm] 

[C]La la la la la la [Fm]la la la la [D#]

[Fm]I made this song Cause I [Bbm]love you 
[C]I made this song just for [Fm]you [D#]
[Fm]I made this song deep in [Bbm]my heart";

$output = '';

$inputLines = explode(PHP_EOL,$input);

foreach($inputLines as $line){
    $output .='<div class="line">';

    if (!strlen($line)){
        $output .= '&nbsp;';
    }
    else{
        $inputWords = explode(' ',$line);

        foreach($inputWords as $word){
            if (preg_match('/^\[(.+)\](.+)$/', $word, $parts)){
                $output .='<span class="group"><span class="top">'.$parts[1].'</span><span class="bottom">'.$parts[2].'</span></span>';
            }
            elseif(preg_match('/^\[(.+)\]$/', $word, $parts)){
                $output .='<span class="group"><span class="top">'.$parts[1].'</span><span class="bottom">&nbsp;</span></span>';
            }
            else{
                $output .='<span class="group"><span class="top">&nbsp;</span><span class="bottom">'.$word.'</span></span>';
            }
        }
    }

    $output .='</div>';

}
die ($output);

这里做的事情很简单。该脚本仅通过将其包装在 HTML 中来赋予和弦数据意义。定位和表示是用 CSS 定义的。

它还表明您在将示例和弦转换为示例输出的方式上存在一点错误。 Fm D# 第 5 行似乎有一点偏差。至少我希望如此。

添加:

为什么您的代码不起作用。

确实如此。不起作用的是它的介绍。你数了一行中的字母,并在另一行中用空格替换了它。有两件事在这里不起作用:

  1. 在基本 HTML 中,多个连续的空格在浏览器 View 中减少为一个
  2. 通常任何浏览器的标准字体都不是等宽的。因此,没有简单的方法可以用相同宽度的空格替换字符。

那你会怎么做呢?

  1. 通过替换为不间断空格 ( ) 而不是简单的空格,您可以确保所有空格都实际显示在浏览器 View 中。正确地做到这一点意味着将 white-space: pre; 设置为一种样式,以便真正识别空白。
  2. 设置等宽字体 (font-family: monospace;) 以确保您的替换字体对齐。

就是这样:

<style>
body{
        font-family: monospace;
        white-space: pre;
</style>

<?php 


function parse_song($content){
    $lines = explode(PHP_EOL, $content); //explode lines

    foreach($lines as $key=>$line){
        $chords_line = line_extract_chords($line);
        $lines[$key] = implode("\n\r",(array)$chords_line);
    }

    return implode("\n\r",$lines);
}

function line_extract_chords($line){

    $line_chords = null; //text line with chords, used to compute offsets
    $line_chords_html = null; //line with chords links
    $found_chords = array();

    $line = html_entity_decode($line); //remove special characters (would make offset problems)

    preg_match_all("/\[([^\]]*)\]/", $line, $matches, PREG_OFFSET_CAPTURE);

    $chord_matches = array();

    if ( $matches[1] ){
        foreach($matches[1] as $key=>$chord_match){

            $chord = $chord_match[0];


            $position = $chord_match[1];
            $offset= $position;
            $offset-= 1; //left bracket
            $offset-=strlen($line_chords); //already filled line

            //previous matches
            if ($found_chords){
                $offset -= strlen(implode('',$found_chords));
                $offset -= 2*(count($found_chords)); //brackets for previous chords
            }

            $chord_html = '<a href="#">'.$chord.'</a>';

            //add spaces
            if ($offset>0){
                $line_chords.= str_repeat(" ", $offset);
                $line_chords_html.= str_repeat(" ", $offset);
            }

            $line_chords.=$chord;
            $line_chords_html.=$chord_html;
            $found_chords[] = $chord;

        }

    }

    $line = htmlentities($line); //revert html_entity_decode()

    if ($line_chords){
        $line = preg_replace('/\[([^\]]*)\]/', '', $line);
        return array($line_chords_html,$line);
    }else{
        return $line;
    }

}

$input = "[D#] [G#] [Fm] 
[C#] [Fm] [C#] [Fm] [C#] [Fm] 

[C]La la la la la la [Fm]la la la la [D#]

[Fm]I made this song Cause I [Bbm]love you 
[C]I made this song just for [Fm]you [D#]
[Fm]I made this song deep in [Bbm]my heart";



die(parse_song($input));

我删除了 self:: 引用以使其独立工作。

所以您实际上并没有在此处编写任何错误代码。你只是搞砸了结果的呈现。

尽管如此,您最终还是得到了一段毫无意义、几乎无法解析(可能是为了解释)的文本。解析输入的步骤应着重于赋予数据意义。例如,如果那是 HTML 或 XML 标记甚至 JSON 的方式,都没有关系。但是您应该将纯文本转换为结构化数据。

这样你就可以很容易地设计它。您可以识别整个结构的单个部分或过滤掉它们。

关于php - 使用 PHP 从选项卡中提取和弦,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/33770714/

相关文章:

php - TIMESTAMPDIFF 适用于日但不适用于分钟

python - 使用 findall() 和 search() 的奇怪正则表达式问题

java - 如何打印与正则表达式不匹配的文件名列表? java 8

PHP:preg_match_all首先匹配内括号?

php - `Editable-Changable` 删除语句中的变量

php - 如何从电子邮件链接或网页打开 Outlook 日历?

php - 为 LDAPS 连接验证自签名证书

python - 使用正则表达式以任意顺序匹配两个单词

php - 通过 preg_match_all 返回的数组循环执行 mysql 查询

php - 正则表达式匹配所有字符,包括回车符