[英]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 .= ' ';
}
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);
这里做的很简单。 该脚本仅通过将其包装在HTML中来为和弦数据赋予意义。 使用CSS定位和表示。
此外,它还演示了示例和弦转换为示例输出的方式有一点错误。 Fm D#
5行中的Fm D#
似乎是一个关闭点。 至少我希望如此。
加:
为什么你的代码不起作用。
它实际上做到了。 什么不起作用的是它的介绍。 你在一行中计算了字母,用另一行中的空格替换了它。 正如您所料,这里有两件事无效:
那你怎么办呢?
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,例如,无关紧要。 但是你应该将纯文本转换为结构化数据。
这样你就可以轻松地设计它。 您可以识别整个结构的单个部分或将其过滤掉。
好吧,根据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 = " "; //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);
?>
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.