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

我正在试图弄清楚如何使用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定位和表示.

此外,它还演示了示例和弦转换为示例输出的方式有一点错误.第5行中的Fm D#似乎是一个关闭点.至少我希望如此.

加:

为什么你的代码不起作用.

它实际上做到了.什么不起作用的是它的介绍.你在一行中计算了字母,用另一行中的空格替换了它.正如您所料,这里有两件事无效:

>在基本HTML中,多个连续的空格在brwoser视图中减少到一个
>通常任何浏览器的标准字体都不是等宽字体.因此,没有简单的方法来替换具有相同宽度的空格的字符.

那你怎么办呢?

>通过替换非中断空格()而不是简单的空格,您可以确保所有空白空间实际上都在浏览器视图中表示.正确地做它意味着设置white-space:pre;作为一种风格,所以白色空间得到了实际认可.
>设置等宽字体(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 :: reference以使其独立运行.

所以你实际上并没有在这里编码任何错误.你刚搞砸了结果的陈述.

尽管如此,你最终会得到一个毫无意义的,几乎无法解释的(也许是解释性的)文本.解析输入的步骤应该着重于赋予数据含义.如果这是HTML或XML标记的方式,甚至是JSON,例如,无关紧要.但是你应该将纯文本转换为结构化数据.

这样你就可以轻松地设计它.您可以识别整个结构的单个部分或将其过滤掉.

上一篇:Python中的字符串以及正则表达式


下一篇:Linux-close_on_exec标志位