【问题标题】:Java Splitting a string with multiple delimiters, some of which are 2-character sequencesJava使用多个分隔符拆分字符串,其中一些是2字符序列
【发布时间】:2016-07-27 12:32:39
【问题描述】:

这里的老读者,但第一次发帖!我正在从事一个大学项目,该项目涉及使用 Java 来处理以基于文本的 abc 符号标准编写的传统音乐旋律的转录(如果您有兴趣,请参阅 here 以获取有关 abc 标准的快速解释)。

我想获取表示为字符串的整个曲调转录的主体,并将其拆分为单独的小节(即,分成字符串数组,每个小节一个字符串)。 abc 标准有许多不同的symbols and combinations of symbols 用于分隔条。这些符号是:

|   
|]  
||  
[|  
|:  
:|  
::

我的想法是使用带有 String.split() 方法的正则表达式将下面的 tuneBody 字符串分解为 arrayOfBars 字符串数组。我的正则表达式如下,旨在尝试找到上述任何可用于分隔音乐中的小节的符号。

import java.util.Arrays;

public class TroubleshootRegex 
{
    //Split the tuneBody into individual bars
        public static void main(String[] args) 
        {
        //The musical notes from an abc tune transcription
        String tuneBody = "|:G3 GAB|A3 ABd|edd gdd|edB dBA|\nGAG GAB|ABA ABd|edd gdd|BAF G3:|\nB2B d2d|ege dBA|B2B dBG|ABA AGA|\nBAB d^cd|ege dBd|gfg aga|bgg g3:|";        

        //The body of the tune after being split into individual bars
        String[] arrayOfBars;

        //This regex is my attempt to look for all the possible bar delimiters defined in the abc standard
        String abcBarDelimiters = "[\\|]|\\|\\||\\[\\||\\|:|:\\||::|\\|]";
        arrayOfBars = tuneBody.split(abcBarDelimiters);
        System.out.println(Arrays.toString(arrayOfBars));
        }
}

不幸的是,当我运行上述程序时,我最终遇到了几个问题。其中一个问题是我在数组的开头得到一个空字符串,但一些研究表明这是一个已知问题,所以我会想办法解决这个问题。尽管我自己似乎无法弄清楚的更大问题是,我最终在音乐的第一小节中包含了一个冒号,而这个应该作为如果一切都按预期工作,则拆分字符串时的初始分隔符。即我希望在字符串拆分期间删除来自tuneBody 的初始“|:”分隔符。这是输出:

[, :G3 GAB, A3 ABd, edd gdd, edB dBA,   
GAG GAB, ABA ABd, edd gdd, BAF G3,   
B2B d2d, ege dBA, B2B dBG, ABA AGA,   
BAB d^cd, ege dBd, gfg aga, bgg g3]  

我假设这意味着我的正则表达式中可能存在某种问题,但对于我的生活,我似乎无法弄清楚实际问题是什么,我开始交叉 -眼睛看着它!似乎是匹配开头的单个管道字符作为分隔符,而不是匹配字符序列 |:

如果任何真正了解正则表达式的人能告诉我为什么我的似乎没有做我想做的事,或者如何让它看到 |: 序列作为一个整体作为分隔符,我将不胜感激, 而不是分隔符后跟冒号。

提前致谢!

【问题讨论】:

  • 试试String abcBarDelimiters = "\\[\\||\\|[:|\\]]|:[:|]|\\|";。它会产生预期的输出吗?
  • 你让我免于扯掉头发。谢谢你,先生。现在我只需要弄清楚为什么你的有效而我的无效;-)
  • 顺便说一句,您链接的页面指出Abc parsers should be quite liberal in recognizing bar lines. In the wild, bar lines may have any shape, using a sequence of | (thin bar line), [ or ] (thick bar line), and : (dots), e.g. |[| or [|::: . - 这意味着使用正则表达式可能不适合该任务。好像:|:[|] 之类的分隔符也可能被允许使用,因此如果您的数据可能 包含这些分隔符,您的表达式最终可能会过于复杂且仍然不完整。
  • @Thomas,是的,我自己读过那部分,但根据我自己对 abc 符号的了解,我使用的大多数曲调源都包含标准的小节线,这个项目是我自己的学术练习,而不是供一般公众使用,所以我目前并不太担心。如果我在项目结束时有空闲时间,我可能会回去看看添加某种例程来捕捉非标准的条形线并用标准形式替换它们。

标签: java regex string split


【解决方案1】:

其中一个问题是我在数组的开头得到一个空字符串,但一些研究表明这是一个已知问题,所以我会想办法解决这个问题。

问题是您的字符串以分隔符开头,因此它将创建一个空字符串作为拆分的第一个元素。如果您有两个连续的定界符,也会发生同样的情况,例如...|::|...。为了解决这个问题,您可以删除不需要的空字符串,例如通过使用列表而不是数组。

虽然我自己似乎无法弄清楚的更大问题是,我最终在音乐的第一个小节中包含了一个冒号,而在拆分时这应该作为初始分隔符的一部分被过滤掉如果一切都按预期工作,则字符串。即,我希望在字符串拆分期间删除来自 tuneBody 的初始“|:”分隔符。

我在这里不完全确定(但很确定):问题是单管道是您的正则表达式中的第一个选项,因此它与|: 中的管道匹配。为了解决这个问题,将单个管道放在最后就足够了。

您还可以简化您的正则表达式,因为您不需要字符类。因此这应该有效:

String abcBarDelimiters = "\\|\\||\\[\\||\\|:|:\\||::|\\|\\]|\\|";

【讨论】:

  • 您可以使用字符类将模式缩小一点,以使其更快地工作:String abcBarDelimiters = "\\[\\||\\|[:|\\]]|:[:|]|\\|";
  • 我不知道我是否遗漏了什么,但从我的角度来看,使用字符类会更好阅读(摆脱所有这些\\ )。正则表达式为:"[:][:|]|[|][\\]|:]|[\\[][|]|\\|"
  • @WiktorStribiżew 是的,只要您知道它们的作用,您就可以使用它们 - 我不确定在 OP 的情况下(还)。
  • @KevinEsche 是的,至少由于转义较少,它看起来更具可读性 - 但老实说,表达仍然难以阅读;) - 问题是 OP 可能不喜欢阅读像 @ 这样的部分987654328@ 所以毕竟原始版本对他来说可能更具可读性。
  • 谢谢大家。我通过粘贴 \[\\||\\|[:|\]]|:[:|]|\\| 慢慢掌握了 Wiktor 的答案这里xenon.stanford.edu/~xusch/regexp 并查看输出。我想我需要去学习更多关于正则表达式的知识,直到现在我通常都避免使用!
【解决方案2】:

为了让正则表达式初学者的眼睛更轻松,请尝试以下操作:

public static void main(String[] args)  {
    //The musical notes from an abc tune transcription
    String tuneBody = "|:G3 GAB|A3 ABd|edd gdd|edB dBA|\nGAG GAB|ABA ABd|edd gdd|BAF G3:|\nB2B d2d|ege dBA|B2B dBG|ABA AGA|\nBAB d^cd|ege dBd|gfg aga|bgg g3:|";        

    //The body of the tune after being split into individual bars

    String re1 = "\\|[\\]\\||:]?";  // |, |], |:
    String re2 = "\\[\\|";          // [|
    String re3 = ":[\\|:]";         // :|, ::

    String abcBarDelimiters = "(" + re1 + "|" + re2 + "|" + re3 + ")";

    String[] arrayOfBars = tuneBody.split(abcBarDelimiters);
    System.out.println(Arrays.toString(arrayOfBars));
}

...正如托马斯已经说过的那样,开头的空字符串是由于输入以分隔符开头。

【讨论】:

  • 不,?很贪心,??会很不情愿。这按预期工作。
猜你喜欢
  • 2012-09-04
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-10-24
  • 2011-12-26
  • 2018-02-27
  • 1970-01-01
相关资源
最近更新 更多