我正在努力弄清楚如何使用 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 .= ' ';
}
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 行似乎有一点偏差。至少我希望如此。
添加:
为什么您的代码不起作用。
确实如此。不起作用的是它的介绍。你数了一行中的字母,并在另一行中用空格替换了它。有两件事在这里不起作用:
- 在基本 HTML 中,多个连续的空格在浏览器 View 中减少为一个
- 通常任何浏览器的标准字体都不是等宽的。因此,没有简单的方法可以用相同宽度的空格替换字符。
那你会怎么做呢?
- 通过替换为不间断空格 ( ) 而不是简单的空格,您可以确保所有空格都实际显示在浏览器 View 中。正确地做到这一点意味着将
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::
引用以使其独立工作。
所以您实际上并没有在此处编写任何错误代码。你只是搞砸了结果的呈现。
尽管如此,您最终还是得到了一段毫无意义、几乎无法解析(可能是为了解释)的文本。解析输入的步骤应着重于赋予数据意义。例如,如果那是 HTML 或 XML 标记甚至 JSON 的方式,都没有关系。但是您应该将纯文本转换为结构化数据。
这样你就可以很容易地设计它。您可以识别整个结构的单个部分或过滤掉它们。
关于php - 使用 PHP 从选项卡中提取和弦,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/33770714/