Warning: file_get_contents(/data/phpspider/zhask/data//catemap/1/php/297.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
使用PHP从选项卡中提取和弦_Php_Regex_Preg Match All_Flags - Fatal编程技术网

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

使用PHP从选项卡中提取和弦,php,regex,preg-match-all,flags,Php,Regex,Preg Match All,Flags,我想弄清楚如何使用preg_match_all和preg_OFFSET_CAPTURE解析音乐(文本)选项卡 输入: 我试图获得的输出: 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

我想弄清楚如何使用preg_match_all和preg_OFFSET_CAPTURE解析音乐(文本)选项卡

输入:

我试图获得的输出:

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\u extract\u chord中存在一些错误,它的工作方式与应有的不一样

有什么想法吗

<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;
    }

}
?>

身体{
字体系列:monospace;
空白:预处理;

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

<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);
我删除了
self::
引用以使其独立工作

所以你在这里并没有写错代码,只是把结果的表达搞砸了

尽管如此,最终还是会得到一段毫无意义、几乎不可解析(可能是为了解释)的文本。解析输入的步骤应该侧重于给出数据的含义。如果这是以HTML或XML标记甚至JSON的方式进行的,则无所谓。但您应该将纯文本转换为结构化数据


通过这种方式,你可以很容易地设计它的样式。你可以识别整个结构的单个部分,或者将它们过滤掉。

好的,我终于找到了一种方法,根据Stefan的答案,让它工作起来,但经过了调整


宋先生{
字体系列:monospace;
空白:预处理;
边缘底部:0.75雷姆;
}
.ugs歌曲行块{
显示:内联块;
}
.ugs歌曲行块。顶部,
.ugs歌曲行块。底部{
显示:块;
}

我开始像你一样做,但是(我不记得为什么),我决定在没有CSS的情况下使用纯代码。这在某种程度上看起来更干净。我仍然想知道为什么我的代码是错误的……无论如何,这是一种很好的方法。我不知道你在这里的实际使用情况,但为什么你会选择在纯代码中进行,而不是将其转换为结构化数据,并将表示层留给表示工具?我补充了一个解释关于你的代码出了什么问题…基本上没什么。看看我更新的答案。嘿,谢谢你的答案和评论。我想你是对的。我应该“把演示层留给演示工具”但是如果我理解正确的话,你的代码会先拆分每一行,然后再拆分每个单词,并检查一个和弦是否与一个单词相关?如果一个单词上有几个和弦呢?例如[Em]理解[a]和?
<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);
<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));
<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);
?>