如何在不切断单词的情况下,根据特定长度拆分字符串?(要求结果字符串长度不一定相等)

5

当字符串长度为35时,我想将类似于“Rupees Two Hundred Forty One and Sixty Eight only”这样的字符串分成两部分。我尝试使用以下代码来拆分字符串:

String text = "Rupees Two Hundred Forty One and Sixty Eight only";
List<String> parts = new ArrayList<>();
int length = text.length();
for (int i = 0; i < length; i += 35) {
    parts.add(text.substring(i, Math.min(length, i + size)));

但输出结果是这样的。
[Rupees Two Hundred Forty One and Si, xty Eight only]
但我想要将字符串分割成这样。
[Rupees Two Hundred Forty One and, Sixty Eight only]
在分割字符串时没有断词。每次根据账单金额的不同,字符串也会发生变化。

@AbdulRashidA,您是否要每次在“and”处切割? - Mustahsan
4
实际目标似乎很清晰:给定一些文本,将文本分成单独的行,使得没有一行超过35个字符。但是当某个单词不符合给定的范围时会发生什么情况呢,即如果它的长度超过了35个字符?应该总是在空格' '处拆分文本,还是在任何空白处 - 或者甚至在其他字符(如 '-')处拆分?如果是这样,这些字符可能应该出现在输出中... 这里存在许多自由度和注意事项... - Marco13
请注意:“不多于35个字符”的限制。 - MC Emperor
@Abra 不是的,这是对Marco13所说的内容的强调。因为问题本身相当模糊。设定行长的最大值似乎是合理的,而不是设定最小值。OP的示例似乎遵守了这个最大值。 - MC Emperor
显示剩余4条评论
6个回答

2

您可能无法完全做到这一点。但可以使用String.indexOf()方法从第35个字符开始查找第一个空格。然后使用substring方法将字符串分割。

      String text = "Rupees Two Hundred Forty One and Sixty Eight only";
      int i = text.indexOf(" ", 35);
      if (i < 0) {
         i = text.length();
      }
      String part1 = text.substring(0,i).trim();
      String part2 = text.substring(i).trim();

这里有一种替代方法。它尚未完全检查边界情况。
      String[] words = text.split(" ");
      int k;
      part1 = words[0];
      for (k = 1; k < words.length; k++) {
         if (part1.length() >= 35 - words[k].length()) {
            break;
         }
         part1 += " " + words[k];
      }
      if (k < words.length) {
         part2 = words[k++];
         while (k < words.length) {
            part2 += " " + words[k++];
         }
      }
      System.out.println(part1);
      System.out.println(part2);


当我使用这段代码时,字符串在“Sixty”之后被截断,“xty”超出了页面边缘。这是用于在热敏打印机上打印账单,页面宽度为80毫米。 - Abdul Rashid A
@AbdulRashidA,如果在35个字符内没有单词边界,应该怎么处理? - Holger
@ WJS,感谢您宝贵的答案。对我来说很有效。再次感谢。 - Abdul Rashid A

1

+35的位置上搜索首选位置。需要考虑的一件事情是,在没有这样的位置时,即单词超过指定大小时应该发生什么。以下代码将强制执行大小限制,如果找不到好的位置,则在单词中间断开:

List<String> parts = new ArrayList<>();
int size = 35, length = text.length();
for(int i = 0, end, goodPos; i < length; i = end) {
    end = Math.min(length, i + size);
    goodPos = text.lastIndexOf(' ', end);
    if(goodPos <= i) goodPos = end; else end = goodPos + 1;
    parts.add(text.substring(i, goodPos));
}

如果断点发生在空格字符处,则空格将从结果字符串中删除。

1
@RavindraRanwala 我尽可能保持表单与 OP 的代码接近。您可以将变量移入循环,但对于 size,我不会这样做,因为它类似于参数,不应与其他临时变量混淆。还有一个选项是声明它为 final,但将其移入循环时无法使用。 - Holger
1
@HadiJ 不,甚至一丁点也不是。除非我们谈论的是程序员的表现,因为将其放在外面可以让他们轻松地看到这是一个可以适应其他用例的值。 - Holger
@Holger,谢谢先生,几天前我读了一篇文章(现在找不到了!)说在循环中声明size会降低性能,因为在循环的每一步中它都会计算大小。我有疑问。 - Hadi J
1
当将其放置在for循环的初始化器中时,就像Ravindra Ranwala建议的那样,它也只会被评估一次。当直接将其放入条件中时,意味着需要为每个迭代重新评估它,但即使如此,影响也很小,除非我们谈论的是像C这样的编程语言,其中获取大小需要对字符串进行迭代。在Java中,Stringlength()意味着只读取一个final字段,并且在循环中重复发生时将被内联化。(即使对于C的字符串,优化器也可能会处理它)。 - Holger
@Holger,感谢您的完整解释。 - Hadi J
显示剩余2条评论

0

我找到了一个替代方案,使用Apache commons-lang3:

import java.util.Arrays;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.text.WordUtils;

class Example {

    public static void main(String[] args) {
        String text = "Rupees Two Hundred Forty One and Sixty Eight only";
        String wrappedText = WordUtils.wrap(text, 35, "\n", false);
        String[] lines = StringUtils.split(wrappedText, "\n");
        System.out.println(Arrays.asList(lines));
        // Outputs [Rupees Two Hundred Forty One and, Sixty Eight only]
    }
}

注意:如果您的输入中有换行符,请最好将其删除。


0

你可以找到 "and" 的索引,然后从字符串的开头到 "and" 的索引位置进行子字符串截取。

 int i = text.indexOf("and") + 3;
 String part1 = text.substring(0,i);
 String part2 = text.substring(i).trim();

0

我会使用 StringBuilders 从头开始构建字符串。以下是带有一些注释的示例:

    String text = "Rupees Two Hundred Forty One and Sixty Eight only For seven thousand chickens";
    String split[] = text.split(" "); // Split by space
    // One SB for each sentence
    StringBuilder sentence = new StringBuilder();
    // One SB for the total String
    StringBuilder total = new StringBuilder();
    for (int i = 0; i < split.length; i++) {
        String word = split[i];
        // Check if that words fits to sentence
        if (sentence.length() + word.length() <= 35) {
            sentence.append(word);
            sentence.append(" ");
        } else {
            total.append(sentence.toString().trim());
            total.append(", ");
            // Flush sentence to total and start next sentence
            sentence = new StringBuilder();
            sentence.append(word);
            sentence.append(" ");
        }
    }
    //Add any leftover
    if (sentence.length() > 0)
        total.append(sentence.toString().trim());
    System.out.println(total.toString());

输出结果为:

241.68卢比,仅限于七千只鸡


0

我认为你可以使用while循环来计算持有最后一个空格字符的单词:

public static List<String> split(String str, int length) {
    List<String> res = new ArrayList<>();
    int prvSpace = 0;
    int from = 0;

    while (prvSpace < str.length()) {
        int pos = str.indexOf(' ', prvSpace + 1);

        if (pos == -1) {
            res.add(str.substring(from));
            prvSpace = str.length();
        } else if (pos - from < length)
            prvSpace = pos;
        else {
            res.add(str.substring(from, prvSpace));
            from = prvSpace + 1;
        }
    }

    return res;
}

演示:

in: "RupeesTwoHundredFortyOneandSixtyEightonly"
out: ["RupeesTwoHundredFortyOneandSixtyEightonly"]

in: "Rupees Two Hundred Forty One and Sixty Eight only"
out: ["Rupees Two Hundred Forty One and", "Sixty Eight only"]

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接