【问题标题】:split a string in java into equal length substrings while maintaining word boundaries在保持单词边界的同时将java中的字符串拆分为等长的子字符串
【发布时间】:2014-11-09 07:05:12
【问题描述】:

如何在保持单词边界的同时将字符串分割成最大字符长度的相等部分?

例如,如果我想将字符串“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;
}

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

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

【问题讨论】:

  • 你能再添加一个输入/输出的例子吗?
  • 当然,例如“需要速度紧追”,最大字符范围指定为 16 ...我需要根据单词边界拆分字符串,因此输出应该是“需要速度”和“紧追”,但目前的实现是我有我得到“极品飞车h”和“ot追求”
  • 所以规则是在最大字符范围或最大字符范围之前的空白处分割?如果第一个单词比字符范围长怎么办?你在中间分开吗?示例:“reallylongwordisfirst and here are several regular words”,长度为 7,您是否期望:“reallylongwordisfirst”“和”“here”“是”“several”“regular”“words”?
  • 我的最大长度为 4000 个字符。我想知道是否有一个包含 4000 个字符的单词,但无论如何,这适用于 android 文本到语音引擎,如果它会混淆单词的发音单词边界没有被考虑在内,另一方面它也有一个最大范围的字符,它可以一次接受......所以我希望现在你能看到我的困境
  • 您是否允许对超过限制的单词进行拆分?例如,如果您将最大字符数设置为7,应该如何拆分"hohohohoho merry Christmas

标签: java string


【解决方案1】:

如果我正确理解了您的问题,那么这段代码应该可以满足您的需要(但它假定 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 标志它现在可以表示任何字符 - 你可能会得到如果您想开始单独拆分每个句子,请摆脱此方法参数,因为它的开头无论如何都会打印在新行中
    • {1,10} - 这是让前面描述的元素出现 1 到 10 次的量词(默认情况下会尝试找到匹配重复的最大数量),
    • .{1,10} - 所以根据我们刚才所说的,它只是代表“1到10个任意字符”
    • ( ) - 括号创建 groups,结构允许我们保存匹配的特定部分(这里我们在 \\s* 之后添加括号,因为我们只想使用空格后面的部分)
  • (?=\\s|$) - 是look-ahead 机制,它将确保.{1,10} 匹配的文本将在其后:

    • 空格(\\s

      OR(写成|

    • 在它之后的字符串$ 的结尾。

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

【讨论】:

  • 很快就会包含这个正则表达式的解释,现在测试它是否是你想要的,如果它有效,请告诉我。
  • 谢谢你它工作得很好......还有一个问题我已经能够将各个字符串组放入一个列表中......但是有一种方法可以指定 arralist 大小......我当使用长度值预初始化数组列表时听到它更快
  • 如果你想获得更好的性能,你会希望避免调整 ArrayList 大小的过程(创建比当前数组大 2 倍的数组,它将存储当前元素和新元素,这对于大数组来说可能是昂贵的过程)。为避免这种情况,您可以使用大于预期元素数量的大小来初始化列表,因此可以使用 new ArrayList((int) (1.5 * text.length()) / size) 之类的东西来初始化它。
  • 我将 text.length()/size 的结果相乘,因为数组需要为无法在单个标记中使用的字符留出额外空间,例如 not importnant 单词 important 需要单独放置令牌,因为它太长了。
  • 这很酷——我总是忘记\\Gs/Lenght/Length/gi 虽然。
【解决方案2】:

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

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:关于所有输入词预计短于设定限制的评论,是在我想出这个解决方案之后提出的;)

【讨论】:

  • 如果整个字符串不包含空格,此解决方案也有效。它只是拆分字符串。
猜你喜欢
  • 2011-04-15
  • 1970-01-01
  • 1970-01-01
  • 2011-04-11
  • 1970-01-01
  • 2021-09-07
相关资源
最近更新 更多