Javascript 正则表达式:第二次出现块:ABC.js 乐谱

Javascript Regex: Second occurrence of block: ABC.js music notation

ABC是一种乐谱;我正在研究将其作为应用程序的一部分进行解析的模式。

有时一个曲调的多个演绎版在一个 ABC 文件中,而我只需要获取第一个演绎版——或者在理想情况下我指定的任何演绎版。再现的开始由 X: 字符串表示。

不可能事先知道一个文件中有多少演绎版。

在Javascript中,我如何return,例如,下面示例中的第一个再现(从第一个 X: 开始到第二个开始),以某种方式如果没有第二个,return第一个,如果有两个以上的演绎,return第一个。

到目前为止,我的工作产生了 ([\s\S]*)(?=X:),它在两个演绎示例中成功,但在一个或两个以上的演绎中失败。

向先行添加“或”结束文件条件可让单个再现案例起作用,但在一个和三个再现案例上失败,例如\([\s\S]*)(?=X:|$)

任何帮助表示赞赏...解析 ABC 的好方法将被许多人使用。

两个版本的示例如下所示——对于三个版本的示例,只需在末尾添加一行带有 X: 的行,对于单个版本,则从第二个版本中删除所有内容 X:

编辑:人们非常友好地要求提供更好的例子,但他们不适合发表评论,所以这里有一些

破碎的承诺很有趣,因为它有不止一个 ABC,而且它们没有按顺序编号:

X:56
T:Broken Pledge, The
R:reel
D:De Dannan: Selected Reels and Jigs.
Z:Also played in Edor, see #734
Z:id:hn-reel-56
M:C|
K:Ddor
dcAG ADDB|cAGF ECCE|D2 (3EFG Addc|AcGc Aefe|
dcAG FGAB|c2Bd cAGE|D2 (3EFG AddB|cAGE FDD2:|
|:dcAG Acde|~f3d ecAB|cAGE GAcd|ec~c2 eage|
dcAG Acde|fedf ecAG|~F3G AddB|cAGE FDD2:|
P:"Variations:"
|:dcAG ~A3B|cAGF ECCE|DEFG Addc|(3ABc Gc Aefe|
dcAG FGAB|c2Bd cAGE|DEFG AddB|A2GE FDD2:|
|:dcAG Acde|~f3d ecAB|cAGE GAcd|ec~c2 eage|
dcAG Acde|~f3d ecAG|FEFG AddB|A2GE FDD2:|

X:2
T:Broken Pledge, The
M:C
L:1/8
Q:250
K:D
dcAG A2 dB | cAGF EDC2 | DEFG Ad ~d2 | AcGc Adfe |
dcAG A2 dB | cAGF EDC2 | DEFG Ad ~d2 | AcGc ADD2 :|
|: dcAG A2 de | fedf edAB | cAGE GAcd | ec ~c2 eage |
dcAG A2 de | fedf edcA | F3 E FGAB | cAGE {F}ED D2 :||

Huish the Cat 很有趣,因为它有很多版本,而且编号都一样。你可以看到 X:whatever 完全是任意的:

X:1
T:Huish the Cat
M:6/8
L:1/8
N:”Author and date unknown.”
R:Air
Q:"Quick"
S:Byrne, the harper, 1802
B:Bunting – Ancient Music of Ireland (1840, p. 3)
Z:AK/Fiddler’s Companion
K:C
(G>A).G c2(e|d<).d.A c2z|(G>A).G .c2 d|(ec).A .A2G|
(G>A).G .c2(e|d<).d.A .c2e|(g>f).e .f2d|(ec).A A2G:|
|:(gf).e .f2d|(ed).c .f2d|(gf).e .f2d|(ec).A A2G|
(gf).e .f2d|(ed).c .f2.d|(G>A).G f2d|(ec).A [F2A2]G:|]

X:1
T:Hunt the Cat
M:6/8
L:1/8
R:Jig
Q:”Allegro”
B:William Forde – 300 National Melodies of the British Isles (c. 1841, p.  26, No. 87)
B: https://www.itma.ie/digital-library/text/300-national-melodies-of-the-british-isles.-vol.-3-100.-irish-airs
N:William Forde (c.1795–1850) was a musician, music collector and scholar from County Cork
Z:AK/Fiddler’s Companion
K:D
A>BA d2f|e<eB d3|A>BA d2e|fdB B2A|
A>BA d2f|e<eB d2f|a>gf g2e|fdB B2A:|
|:agf g2e|FED G2E|agf g2e|fdB B2A|
agf g2e|fed g2e|A>BA g2e|fdB B2A:|]

X:1
T:Huish the Cat
M:6/8
L:1/8
R:Jig
Q:"Quick"
B:P.M. Haverty – One Hundred Irish Airs vol. 1 (1858, No. 87, p. 37)
Z:AK/Fiddler’s Companion
K:C
(G>A).G .c2(e|d<).d.A c2z|(G>A).G .c2d|(ec).A .A2G|
(G>A).G .c2(e|d<).d.A .c2|(g>f).e .f2d|(cA).A A2G:|
|:(gf).e .f2d|(ed).c .f2d|(gf).e .f2d|(ec).A A2G|
(gf).e .f2d|(ed).c .f2.d|(G>A).G f2d|(ec).A [F2A2] G:|]

X:1
T:Huish the Cat
M:6/8
L:1/8
R:Single Jig
S:O'Neill - Dance Music of Ireland: 1001 Gems (1907), No. 382
Z:AK/Fiddler's Companion
K:C
G>AG c2e|d<dA c2e|G>AG c2d|ecA A2c|
G>AG c2e|d<dA c2e|g>fe f2d|ecA A2G:|
|:gfe f2d|edc f2d|gfe f2d|ecA A2G|
gfe f2d|edc f2d|G>AG f2d|ecA A2G:||

X:1
T:Hunt the Cat
M:6/8
L:1/8
B:Roche, vol. 3 (1927, p. 114)
K:Ddor
DED D2A|AGE c3|DED D2A|AGE E2D|
DED D2A|AGE c3|ABc d2B AGE E2D:|
|:dcA AGE|AGE c3|dcA AGE|AGE E2D|
dcA AGE|AGE c3|ABc d2c|AGE E2D:||

LowBack 车很乱,有百分号之类的

X:1
%
T:Lowbacked Car [1], The
M:6/8
L:1/8
R:Air
S:James Goodman (1828─1896) music manuscript collection, 
S:vol. 3, p. 133. Mid-19th century, County Cork
Z:AK/Fiddler’s Companion
K:G
G|G2B B2d|c2A z2F|G2B d2d|d3 z2G|
c2c A2A|B2B G2B|c2A G2F|G3 z2G|
G2c c2e|e2d d2G|G2c c2e|d3 z2G|
G2g !fermata!g2e|e2d dcB|A2G A2B|!fermata!d3 z2A|
GED G2G|G3 z2B|AGE A2A|A3z B/c/|
dcB dcB|gfe !fermata!d2 B/A/|GED G2G|(G3 G2)||
X:1
%
T:Low Backed Car (1)
M:6/8
L:1/8
B:Howe - Musicians's Omnibus No. 2 (p. 107)
Z:AK/Fiddler's Companion
R:G
G|G2B B2d|c3 A2d|G2 B2 d2d|(d3 d2)B|
c2c A2A |B3 G2G A2A F2F|(G3 G2)||d|
d2g g2e|e2d d2B|d2g g2e|(e3 d2)d|
d2g g2e|e2d d2B|BAG A2B|d2c B2A|
.G.E.E .G2G|(G3 G2)B|AGE A2A|A3 ABc|
(.d.c.B) (.d.c.B)|(.a.a.d) .e.d.B|.G.E.D|(G3 G2)|]
X:1
%
T:Low Backed Car [1], The
M:6/8
L:1/8
R:Jig
B:Kerr - Merry Melodies, vol. 2, No. 257  (c. 1880's)
Z:AK/Fiddler's Companion
K:G
D|G2B B2d|d2c A2F|G2B d2d|(d3 d2) B|
cBc A2A|BAB GAB|c2A G2F|(G3 G2):||
B|G2g g2e|e2d d2B|G2g g2e|d3 cBA|
G2g g2e|e2d dcB|A2G A2B|d3 cBA|
GED G2G|(G3 G2)B|AGE A2A|A3 (ABc)|
dcB dcB|Gfe dBA|GED G2G|(G3 G2)||

Lowbacked Car for 6 是单调的模态情况,我们需要将其作为最常见的情况处理:

X:1
T:Jaunting Car for Six
M:9/8
L:1/8
R:Slip Jig
S:Kerr - Merry Melodies, vol. 3, No. 233 (c. 1880's)
Z:AK/Fiddler's Companion
K:A
efe c2c c3|efe cde fga|efe c2c c3|BcB B2c def:|
|:e2a agf ecA|e2a agf e3|e2a agf ecA|BcB B2c def:|| 

抱歉,这是对答案的完全重写。以下函数 return 是您当前感兴趣的信息(它可以扩展为 return 更多信息,例如,作为与 [=11= 共享索引的数组的再现标题]数组)。

function getAbcInfo(abc) {
    let renditions = ('\n' + abc).split(/[\r\n]+(?=[ \t\u00a0]*X[ \t\u00a0]*:[ \t\u00a0]*\d+)/);
    renditions.push(renditions.pop().replace(/[\r\n]+$/, ''))
    renditions.unshift(renditions.shift().replace(/^[\r\n]+/, ''))
    let x = ['']
    let indicesOfX = {'': [0]}
    for (let i = 1; i < renditions.length; i++) {
        let n = renditions[i].match(/^[ \t\u00a0]*X[ \t\u00a0]*:[ \t\u00a0]*(\d+)/)[1]
        x[i] = n
        if (n in indicesOfX) {
            indicesOfX[n].push(i)
        } else {
            indicesOfX[n] = [i]
        }
    }
    return {renditions: renditions, x: x, indicesOfX: indicesOfX}
}

console.log(JSON.stringify(getAbcInfo(brokenPledge)));
// {"renditions":["","X:56…","X:2…"],"x":["","56","2"],"indicesOfX":{"2":[2],"56":[1],"":[0]}}
console.log(JSON.stringify(getAbcInfo(huishTheCat)));
// {"renditions":["","X:1…","X:1….","X:1…","X:1…","X:1…"],"x":["","1","1","1","1","1"],"indicesOfX":{"1":[1,2,3,4,5],"":[0]}}
console.log(JSON.stringify(getAbcInfo(lowbackedCar)));
// {"renditions":["","X:1…","X:1…","X:1…"],"x":["","1","1","1"],"indicesOfX":{"1":[1,2,3],"":[0]}}
console.log(JSON.stringify(getAbcInfo(commonCase)));
// {"renditions":["","X:1…"],"x":["","1"],"indicesOfX":{"1":[1],"":[0]}}
console.log(JSON.stringify(getAbcInfo(brokenPledgeWithoutTheFirstLine)));
// {"renditions":["T:Broken Pledge…","X:2…"],"x":["","2"],"indicesOfX":{"2":[1],"":[0]}}

renditions 数组始终包含索引 0 处第一个 X:(如果有)之前的内容。这通常是空字符串,但它可能是带有标准允许的字段的 header ,或者甚至是完整的再现,如果它的 X: 行被简单地省略(反对标准,但人类不要总是遵循标准)。

从索引 1 开始,renditions 的项目是以 X: 开头的演绎版(实际上允许空格,请参阅正则表达式),并删除尾随换行符。

x 数组与 renditions 数组共享索引,给出每个再现的 X:n 行的 n。由于索引 0 处的“再现”没有 X:n 行(它是“未命名”,或者更确切地说,“未编号”),x 数组在索引 0.

indicesOfX object 允许您在给定 X:nn 的情况下获取 renditions 中的索引数组。换句话说,它反转了 x 数组的 key-value 关系。

如果您想扩展函数以向输出添加一个 titles 数组,请不要忘记您不能简单地匹配 T:,因为您有考虑空格(我使用的正则表达式允许空格、制表符和 non-breaking 空格——不要使用 \s*,因为它包括 \n),也因为 T: 必须 前面有一个换行符,索引 0 处的再现除外,它可以位于字符串的开头。 T: 的文本以换行符 ([\r\n]) 结束。

顺便说一句,您可能希望通过将所有 \r 替换为任何内容来“规范化”换行符,或者,如果您担心换行符周围可能存在旧的 Mac 经典文件 \r,将所有 \r\n 替换为 \n,然后将所有剩余的 \r 替换为 \n。一旦确定周围没有 \r 换行符,就可以使用 ^ 和 [=48= 同时匹配新行的开头和字符串的开头](多行)标志。

如果你想要从X:开始的数据中的第n部分或者从字符串的开头开始,你可以使用捕获组来捕获你想要保留的内容并使用量词来重复n 作为匹配项之前的部分。

Javascript 中的模式,以获取例如带有量词 {3}

的第三部分

Javascript 不支持所有格量​​词,但您可以通过在前瞻中使用捕获组来模拟它,然后使用反向引用匹配它,因为在前瞻中没有回溯。

/^(?:(?=([\s\S]+?(?=X:|$)))){3}/g

模式匹配:

  • ^ 字符串开头
  • (?:非捕获组
    • (?= 正面前瞻
      • ([\s\S]+?(?=X:|$)) 尽可能少地匹配 1+ 次字符,并断言 X: 或字符串末尾向右
    • ) 关闭前瞻
    • </code> 匹配捕获组 1 值的反向引用</li> </ul> </li> <li><code>){3}关闭非捕获组,重复n次,本例为3次

    Regex demo

    如果 X: 应该在换行符之后,您可以在它之前添加一个换行符:

    /^(?:(?=([\s\S]+?(?=\nX:|$)))){2}/g
    

    Regex demo

您标记了 abcjs,所以我假设您正在使用该库。如果这不是一个正确的假设,请无视。

有一个函数可以为您提供有关字符串的元数据。您可以拨打:

var tuneBook = new ABCJS.TuneBook(tunebookString)
var arrayOfTunes = tunebook.tunes;
var firstTune = arrayOfTunes[0];

https://paulrosen.github.io/abcjs/analysis/tune-book.html#number-of-tunes