在Java中使用RegEx时出现模式错误

时间:2009-12-11 06:24:07

标签: java regex

使用正则表达式时,我遇到了问题。 我的要求是:将一个长字符串拆分为最大125个字母,然后在它们之间插入一个换行符。 在分裂时,它不应该在单词之间分开。简而言之,我想将一个字符串拆分为长度为125的小字符串或者在第125个字母之前的字的末尾。希望我没有混淆

我使用一个正则表达式来解决这个问题,并相信我在这方面是绝对的零。 我只得到一个代码和复制粘贴; - )

StringBuffer result = null;  
while(mailBody.trim().length() > 0){  
    Matcher m = Pattern.compile("^.{0,125}\\b").matcher(mailBody);  
    m.find();  
    String oneLineString = m.group(0);  
    if(result == null)  
        result = new StringBuffer(oneLineString);  
    else  
        result.append("\n"+ oneLineString);  
    mailBody = mailBody.substring(oneLineString.length(),
                                  mailBody.length()).trim();  
}    

这是我的代码,除非起始字符串以句号结束(。),否则它会完美运行。 在这种情况下,它会给出一个错误:找不到匹配项。

请帮忙。

此致 Anoop P K

5 个答案:

答案 0 :(得分:2)

我还不能评论,给出的答案是好的。我想补充一点,你应该在循环之前初始化你的StringBuffer并减少复制,启动它至少和原始字符串一样大,如下所示:

StringBuffer result = new StringBuffer(mailBody.length());

然后在循环中,不需要检查result == null

编辑:评论PSpeed的回答...... 需要在每个新行中添加新行以匹配原始行,类似于此(假设结果已按我的建议初始化):

while (m.find()) {
    if (result.length() > 0)
        result.append("\n");
    result.append(m.group().trim());
}

答案 1 :(得分:1)

您可以尝试使用以下内容吗?

Matcher m = Pattern.compile("(?:^.{0,125}\\b)|(?:^.{0,125}$)").matcher(mailBody);  

这里我们使用您的原始匹配或我们匹配一个总长度不超过125个字符的字符串。 (?:X)项是非捕获组,因此我可以使用|大集团的运营商。

See documentation for the Pattern class here。)


附录: @Anoop:非常正确,在自己的行上留下句子结尾的标点符号是不受欢迎的行为。你可以试试这个:

if(result == null)  
   result = new StringBuffer("");

mailBody = mailBody.trim();

while(mailBody.length() > 125) {

    // Try not to break immediately before closing punctuation
    Matcher m = Pattern.compile("^.{1,125}\\b(?![-\\.?;&)])").matcher(mailBody);
    String oneLineString;

    // Found a safe place to break string
    if (m.find()) {

        oneLineString = m.group(0);

    // Forced to break string in an ugly fashion
    } else {

        // Try to break at any word boundary at least
        m = Pattern.compile("^.{1,125}\\b").matcher(mailBody);

        if (m.find()) {

            oneLineString = m.group(0);

        // Last ditch scenario, just break at 125 characters
        } else {

            oneLineString = mailBody.substring(0,124);

        }

    }

    result.append(oneLineString + "\n");
    mailBody = mailBody.substring(oneLineString.length(),
                                  mailBody.length()).trim();  
}

result.append(mailBody);

答案 2 :(得分:1)

不要直接使用正则表达式,而应考虑使用java.text.BreakIterator - 这就是它的设计目的。

答案 3 :(得分:1)

首先,您可以通过更简单的模式和lookAt()方法在技术上获得相同的结果,这使您的意图更加明显。此外,最好将模式编译从循环中拉出来。

我认为你的正则表达式很简单,虽然你可能想明确定义单词中断的意思而不是依赖于单词边界的含义。听起来你想捕捉这个时期并在之后休息但是\ b不会这样做。你可以改为打破空白......

编辑:现在更简单......

StringBuilder result = null;  
Pattern pattern = Pattern.compile( ".{0,125}\\s|.{0,125}" );
Matcher m = pattern.matcher(mailBody);
while( m.find() ) {
    String s = m.group(0).trim();
    if( result == null ) {
        result = new StringBuilder(s);  
    } else {
        result.append(s);
    }
}

...我认为新改进的编辑更简单,仍然可以做你想要的。

如果存在其他可被视为易碎字符的字符,则可以调整模式:

Pattern.compile( ".{0,125}[\\s+&]|.{0,125}" );

......等等。这将允许在空格,+字符和&以chars为例。

答案 4 :(得分:0)

异常不是由你的正则表达式引起的,而是因为你错误地使用了API。在调用find()之前,您应该检查group()方法的返回值 - 这就是您知道匹配成功的方式。

编辑:这就是发生的事情:当你到达最后一段文本时,正则表达式最初匹配到最后。但是\b在该位置无法匹配,因为最后一个字符是句点(或句号),而不是单词字符。所以它回溯了一个位置,然后\b可以匹配最后一个字母和句点。

然后它尝试匹配另一个块,因为mailBody.trim().length()仍然大于零。但这次根本没有单词字符,因此匹配尝试失败,m.find()返回false。但是你没有检查返回值,只需继续调用m.group(0),它会正确抛出异常。您应该使用m.find()作为while条件,而不是使用字符串长度的业务。

事实上,你做的工作比你需要的多得多;如果您正确使用API​​,则可以将代码减少到一行:

mailBody = mailBody.replaceAll(
    "\\G(\\w{125}|.{1,123}(?<=\\w\\b)[.,!?;:/\"-]*)\\s*",
    "$1\n" ).trim();

正则表达式并不完美 - 我不认为这是可能的 - 但它可能做得很好。