将java中的字符串拆分为相等长度的子字符串,同时保持字边界

时间:2014-09-15 17:18:30

标签: java string

如何在保持字边界的同时将字符串拆分为最大字符长度的相等部分?

比如说,如果我想将一个字符串“hello world”拆分成最多7个字符的相等子串,它应该返回给我

"hello "

"world"

但是我当前的实现返回

"hello w"

"orld   "

我使用以下代码从Split string to equal length substrings in Java将输入字符串拆分为等份

public static List<String> splitEqually(String text, int size) {
    // Give the list the right capacity to start with. You could use an array
    // instead if you wanted.
    List<String> ret = new ArrayList<String>((text.length() + size - 1) / size);

    for (int start = 0; start < text.length(); start += size) {
        ret.add(text.substring(start, Math.min(text.length(), start + size)));
    }
    return ret;
}

在将字符串拆分为子字符串时是否可以保持字边界?

更具体地说,我需要字符串拆分算法来考虑空格提供的单词边界,而不是仅仅在分割字符串时依赖字符长度,尽管这也需要考虑但更像是最大范围字符而不是硬编码的字符长度。

2 个答案:

答案 0 :(得分:16)

如果我正确理解您的问题,那么此代码应该满足您的需求(但它假设 maxLenght等于或大于最长的单词

String data = "Hello there, my name is not importnant right now."
        + " I am just simple sentecne used to test few things.";
int maxLenght = 10;
Pattern p = Pattern.compile("\\G\\s*(.{1,"+maxLenght+"})(?=\\s|$)", Pattern.DOTALL);
Matcher m = p.matcher(data);
while (m.find())
    System.out.println(m.group(1));

输出:

Hello
there, my
name is
not
importnant
right now.
I am just
simple
sentecne
used to
test few
things.

"\\G\\s*(.{1,"+maxLenght+"})(?=\\s|$)"正则表达式的简短(或不是)解释:

(让我们记住,在Java \中不仅特殊于正则表达式,而且还在字符串文字中,所以要使用\d之类的预定义字符集,我们需要将其写为"\\d"因为我们需要在字符串文字中转义\

  • \G - 是代表先前创建的匹配结束的锚点,或者如果还没有匹配(当我们刚刚开始搜索时)字符串的开头(与^相同)
  • \s* - 表示零个或多个空格(\s表示空格,*“零或多个”量词)
  • (.{1,"+maxLenght+"}) - 让我们将其拆分为更多部分(在运行时:maxLenght将保留一些数值,如10,因此正则表达式会将其视为.{1,10}
    • .表示任何字符(实际上默认情况下,它可能表示除\n\r之类的行分隔符以外的任何字符,但是由于Pattern.DOTALL标记,它现在可以代表任何字符character - 如果你想分别开始分割每个句子,你可以摆脱这个方法参数,因为无论如何它将以新行打印
    • {1,10} - 这是量词,它允许先前描述的元素出现1到10次(默认情况下会尝试找到匹配重复的最大值),
    • .{1,10} - 基于我们刚才所说的内容,它只代表“1到10个任何字符”
    • ( ) - 括号创建groups,这些结构允许我们保留匹配的特定部分(此处我们在\\s*之后添加了括号,因为我们只想使用部分在空白之后)
  • (?=\\s|$) - 是look-ahead机制,可确保.{1,10}匹配的文字在其后面有:

    • 空格(\\s

      OR(写作|

    • 后面的字符串$的结尾。

感谢.{1,10},我们最多可以匹配10个字符。但是在(?=\\s|$)之后我们要求与.{1,10}匹配的最后一个字符不是未完成单词的一部分(在它之后必须有空格或字符串结尾)。

答案 1 :(得分:3)

非正则表达式解决方案,以防有人更舒服(?)不使用正则表达式:

private String justify(String s, int limit) {
    StringBuilder justifiedText = new StringBuilder();
    StringBuilder justifiedLine = new StringBuilder();
    String[] words = s.split(" ");
    for (int i = 0; i < words.length; i++) {
        justifiedLine.append(words[i]).append(" ");
        if (i+1 == words.length || justifiedLine.length() + words[i+1].length() > limit) {
            justifiedLine.deleteCharAt(justifiedLine.length() - 1);
            justifiedText.append(justifiedLine.toString()).append(System.lineSeparator());
            justifiedLine = new StringBuilder();
        }
    }
    return justifiedText.toString();
}

测试:

String text = "Long sentence with spaces, and punctuation too. And supercalifragilisticexpialidocious words. No carriage returns, tho -- since it would seem weird to count the words in a new line as part of the previous paragraph's length.";
System.out.println(justify(text, 15));

输出:

Long sentence
with spaces,
and punctuation
too. And
supercalifragilisticexpialidocious
words. No
carriage
returns, tho --
since it would
seem weird to
count the words
in a new line
as part of the
previous
paragraph's
length.

它考虑了长于设定限制的单词,因此不会跳过它们(与正在找到supercalifragilisticexpialidosus时停止处理的正则表达式版本不同。)

PS:在我提出这个解决方案之后,关于所有输入词的评论预计会短于设定的限制;)