使用PHP从选项卡中提取和弦
我想弄清楚如何使用preg_match_all和preg_OFFSET_CAPTURE解析音乐(文本)选项卡 输入: 我试图获得的输出:使用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
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 .= ' ';
}
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"> </span></span>';
}
else{
$output .='<span class="group"><span class="top"> </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 .= ' ';
}
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"> </span></span>';
}
else{
$output .='<span class="group"><span class="top"> </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 = " "; //force content if empty !
$chords_html = sprintf('<span class="top">%s</span>',$chords_content);
if (!$words_content = $single_line_chunk[1]) $words_content = " "; //force content if empty !
$words_content = preg_replace('/\s(?=\S*$)/'," ",$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);
?>