繁体   English   中英

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

[英]extract chords from a tab using PHP

我正在试图弄清楚如何使用preg_match_all和PREG_OFFSET_CAPTURE来解析音乐(文本)选项卡。

输入示例

[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行中的Fm D#似乎是一个关闭点。 至少我希望如此。

加:

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

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

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

那你怎么办呢?

  1. 通过替换为非中断空格()而不是简单的空格,您可以确保所有空白空间实际上都在浏览器视图中表示。 正确地做它意味着设置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:: reference以使其独立运行。

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

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

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

好吧,根据Stefan的回答,我终于找到了一种方法让它工作,但是当达到和弦和单词之间的限制时 ,我会调整分割线

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

.ugs-song-line-chunk{
    display: inline-block;
}
.ugs-song-line-chunk .top,
.ugs-song-line-chunk .bottom{
    display: block;
}
</style>

<?php

function parse_song($content){

    $input_lines = explode(PHP_EOL, $content); //explode lines

    $chunks_pattern = '~ \h*
    (?|        # open a "branch reset group"
        ( \[ [^]]+ ] (?: \h* \[ [^]]+ ] )*+ ) # one or more chords in capture group 1

        ( [^[]* (?<=) )  # eventual lyrics (group 2)
      |                      # OR
        ()                   # no chords (group 1)
        ( [^[]* [^[] )   # lyrics (group 2)
    )          # close the "branch reset group"
    ~x';

    $chords_pattern = '/\[([^]]*)\]/';

    //get line chunks
    $all_lines_chunks = null;

    foreach ((array)$input_lines as $key=>$input_line){
        if (preg_match_all($chunks_pattern, $input_line, $matches, PREG_SET_ORDER)) {
            $all_lines_chunks[$key] = array_map(function($i) { return [$i[1], $i[2]]; }, $matches);
        }
    }

    foreach ((array)$all_lines_chunks as $key=>$line_chunks){
        $line_html = null;

        foreach ((array)$line_chunks as $key=>$single_line_chunk){

            $chords_html = null;
            $words_html = null;

            if ($chords_content = $single_line_chunk[0]){

                if (preg_match_all($chords_pattern, $chords_content, $matches, PREG_SET_ORDER)) {

                    $chords_content = null; //reset it

                    foreach ((array)$matches as $match){
                        $chord_str = $match[1];
                        $chords_content.= sprintf('<a class="ugs-song-chord" href="#">%s</a>',$chord_str);



                    }
                }
            }

            if (!$chords_content) $chords_content = "&nbsp;"; //force content if empty !
            $chords_html = sprintf('<span class="top">%s</span>',$chords_content);


            if (!$words_content = $single_line_chunk[1]) $words_content = "&nbsp;"; //force content if empty !
            $words_content = preg_replace('/\s(?=\S*$)/',"&nbsp;",$words_content); //replace last space by non-breaking space (span would trim a regular space)


            $words_html = sprintf('<span class="bottom">%s</span>',$words_content);

            $line_html.= sprintf('<div class="ugs-song-chunk">%s</div>',$chords_html.$words_html);
        }

        $all_lines_html[]=sprintf('<div class="ugs-song-line">%s</div>',$line_html);
    }

    return implode(PHP_EOL,$all_lines_html);

}

$input = "[C]Hush me, tou[C]ch me
[Gm]Perfume, the wind and the lea[C]ves
[C]Hush me, tou[C]ch me
[Gm]The burns, the holes in the she[C]ets";

echo parse_song($input);
?>

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM