我正在使用PHP的simpleXml将screenxml文件显示为屏幕上的乐谱。除非在乐谱的某处改变谱号,否则一切都运作良好。以下是xml文件的摘录,显示了度量27中的谱号更改:
<measure number="27">
<note>
</note>
<note>
</note>
<attributes>
<clef number="2">
<sign>F</sign>
<line>4</line>
</clef>
</attributes>
<note>
</note>
<note>
</note>
<note>
</note>
</measure>
我通过 foreach($ measure-&gt;注释为$ note)来查看每个度量中的注释。我可以通过 if(isset($ measure-&gt; attributes-&gt; clef))来检测音量中的谱号变化,但这并没有告诉我哪里谱号发生变化(在测量的第二个音符之后和最后三个音符之前,在此示例中。)
我查看了simpleXmlIterator,但它似乎没有处理simpleXml对象的摘录(在这种情况下是$ measure)。我试过了:
$sxi = new SimpleXMLIterator($measure);
这给了我以下警告:
警告: SimpleXMLElement :: __ construct():实体:第29行:解析器错误:开始 标签预期,'&lt;'找不到
当我var_dump($measure)
时,我发现它将谱号更改放在notes数组的末尾,但是为它指定了一个数字,可用于确定其在音符序列中的正确位置:
object(SimpleXMLElement)#21 (4) {
["@attributes"]=>
array(2) {
["number"]=>
string(2) "25"
["width"]=>
string(6) "468.94"
}
["note"]=>
array(25) {
...
[20]=>
object(SimpleXMLElement)#43 (5) {
["rest"]=>
object(SimpleXMLElement)#49 (0) {
}
["duration"]=>
string(1) "4"
}
[21]=>
object(SimpleXMLElement)#45 (7) {
["@attributes"]=>
array(2) {
["default-x"]=>
string(6) "293.75"
["default-y"]=>
string(7) "-255.00"
}
["pitch"]=>
object(SimpleXMLElement)#49 (2) {
["step"]=>
string(1) "D"
["octave"]=>
string(1) "4"
}
["duration"]=>
string(1) "2"
}
}
["attributes"]=>
object(SimpleXMLElement)#44 (1) {
["clef"]=>
object(SimpleXMLElement)#49 (3) {
["@attributes"]=>
array(1) {
["number"]=>
string(1) "2"
}
["sign"]=>
string(1) "G"
["line"]=>
string(1) "2"
}
}
}
对象#44(谱号改变)应该在作为对象#43和#45的两个音符[实际上,休息和音符]之间。所以,如果我能找到一种方法来访问那些“对象号”,我的问题就可以解决了。有谁知道如何做到这一点,或者更好的方法来解决这个问题?
答案 0 :(得分:0)
我找到了一个适合我的解决方案。我意识到simpleXml()的返回值是一个对象(不是XML字符串。)所以我不能将它的一部分提供给simpleXmlIterator()并期望除了错误之外的任何东西。
相反,我最终正确地对待$ measure:作为一个对象,并按如下方式迭代它:
if(isset($measure->attributes->clef) && $measureNbr > 0) {
// There is a clef change in this measure!
$clefNbr = $measure->attributes->clef->attributes()->number;
$durationBeforeClefChg=0;
foreach($measure as $property => $value) {
// Count the unchorded durations before the clef change.
if(is_object($value) || is_array($value)) {
foreach($value as $p2 => $v2) {
if(trim($p2) == 'duration'
&& $property == 'note'
&& ((!property_exists($value,'staff'))
|| trim($value->staff) == $clefNbr)
&& !property_exists($value,'chord')) {
$durationBeforeClefChg+= intval(trim($v2));
}
if(trim($p2) == 'clef' && $property == 'attributes') {
$newClef = trim($v2->sign);
break 2;
}
}
}
}
}
答案 1 :(得分:0)
考虑一个XSLT解决方案。这种特殊用途的XML转换语言可以运行preceding-sibling::*
和following-sibling::*
XPath方法,以在新的单独XML文件中检索<notes>
之前和之后的<attributes>
个元素的数量。 / p>
PHP可以运行带有php-xsl类的XSLT 1.0脚本。下面演示了XML呈现特定信息的输出。使用这种方法,不需要使用foreach
逻辑的嵌套if
循环。
输入XML (不同位置的属性和不带谱号的不同小节数)
<?xml version="1.0"?>
<score-partwise version="1.0">
<part-list>
<score-part id="P1">
<part-name>Voice</part-name>
</score-part>
</part-list>
<part id="P1">
<measure number="26">
<note/>
<note/>
<note/>
<note/>
<note/>
<attributes>
<clef number="2">
<sign>F</sign>
<line>4</line>
</clef>
</attributes>
</measure>
<measure number="27">
<note/>
<note/>
<attributes>
<clef number="2">
<sign>F</sign>
<line>4</line>
</clef>
</attributes>
<note/>
<note/>
<note/>
</measure>
<measure number="28">
<note/>
<attributes>
<clef number="2">
<sign>F</sign>
<line>4</line>
</clef>
</attributes>
<note/>
<note/>
<note/>
<note/>
</measure>
<measure number="29">
<note/>
<note/>
<note/>
<note/>
<attributes>
<test>XSLT is cool!</test>
</attributes>
<note/>
</measure>
</part>
</score-partwise>
XSLT (另存为.xsl,可以通过文件或字符串在PHP中解析)
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" indent="yes"/>
<xsl:strip-space elements="*"/>
<xsl:template match="node()|@*">
<xsl:copy>
<xsl:apply-templates select="node()|@*"/>
</xsl:copy>
</xsl:template>
<xsl:template match="measure">
<xsl:copy>
<xsl:copy-of select="@*"/>
<notes_before_clef><xsl:copy-of select="count(attributes[clef!='']/preceding-sibling::note)"/></notes_before_clef>
<notes_after_clef><xsl:copy-of select="count(attributes[clef!='']/following-sibling::note)"/></notes_after_clef>
</xsl:copy>
</xsl:template>
</xsl:stylesheet>
PHP (在.ini文件中启用php-xsl扩展名)
// LOAD XML AND XSLT
$xml = new DOMDocument;
$xml->load('Input.xml'); // OR $xml->loadXML($xmlstring);
$xsl = new DOMDocument;
$xsl->load('XSLT_Script.xsl'); // OR $xsl->loadXML($xslstring);
// INITIALIZE TRANSFORMER
$proc = new XSLTProcessor;
$proc->importStyleSheet($xsl);
// TRANSFORM SOURCE TO NEW TREE
$newXML = $proc->transformToXML($xml);
// ECHO OUTPUT
echo $newXML;
// SAVE OUTPUT TO FILE
file_put_contents('Output.xml', $newXML);
输出XML (解析此文件以满足最终用途需求)
<?xml version="1.0"?>
<score-partwise version="1.0">
<part-list>
<score-part id="P1">
<part-name>Voice</part-name>
</score-part>
</part-list>
<part id="P1">
<measure number="26">
<notes_before_clef>5</notes_before_clef>
<notes_after_clef>0</notes_after_clef>
</measure>
<measure number="27">
<notes_before_clef>2</notes_before_clef>
<notes_after_clef>3</notes_after_clef>
</measure>
<measure number="28">
<notes_before_clef>1</notes_before_clef>
<notes_after_clef>4</notes_after_clef>
</measure>
<measure number="29">
<notes_before_clef>0</notes_before_clef>
<notes_after_clef>0</notes_after_clef>
</measure>
</part>
</score-partwise>
答案 2 :(得分:0)
或者,使用preceding-sibling::*
和following-sibling::*
直接使用PHP的SimpleXMLElement::xpath运行XPath。下面创建一个多维数组, $ clefs ,用度量编号索引,带有两个内部数组,用于 before_notes 和 after_notes :
示例XML
$xml = simplexml_load_string(
'<score-partwise version="1.0">
<part-list>
<score-part id="P1">
<part-name>Voice</part-name>
</score-part>
</part-list>
<part id="P1">
<measure number="26">
<note/>
<note/>
<note/>
<note/>
<note/>
<attributes>
<clef number="2">
<sign>F</sign>
<line>4</line>
</clef>
</attributes>
</measure>
<measure number="27">
<note/>
<note/>
<attributes>
<clef number="2">
<sign>F</sign>
<line>4</line>
</clef>
</attributes>
<note/>
<note/>
<note/>
</measure>
<measure number="28">
<note/>
<attributes>
<clef number="2">
<sign>F</sign>
<line>4</line>
</clef>
</attributes>
<note/>
<note/>
<note/>
<note/>
</measure>
<measure number="29">
<note/>
<note/>
<note/>
<note/>
<attributes>
<test>XSLT is cool!</test>
</attributes>
<note/>
</measure>
</part>
</score-partwise>');
解析代码
$clefs = [];
foreach($xml->xpath('//measure') as $item) {
$measure_num = (integer)$item->xpath('@number')[0];
$clefs['before_notes'][$measure_num] = count($item->xpath("attributes[clef!='']/preceding-sibling::note"));
$clefs['after_notes'][$measure_num] = count($item->xpath("attributes[clef!='']/following-sibling::note"));
}
echo var_dump($clefs);
// array(2) {
// ["before_notes"]=> array(4) {
// [26]=> int(5) [27]=> int(2) [28]=> int(1) [29]=> int(0)
// }
// ["after_notes"]=> array(4) {
// [26]=> int(0) [27]=> int(3) [28]=> int(4) [29]=> int(0)
// }
// }