【问题标题】:Split long lines and Indent and output as so拆分长行并缩进并输出
【发布时间】:2019-04-14 17:04:41
【问题描述】:

我有一个从字符串中删除重复单词的代码。假设我有:

This is serious serious work。我应用代码并得到:This is serious work

这是代码:

 return Arrays.stream(input.split(" ")).distinct().collect(Collectors.joining(" "));

现在我想添加新的约束条件,即如果字符串/行长于 78 个字符,则在有意义的地方中断和缩进,这样行的长度不会超过 78 个字符。示例:

This one is a very long line that runs off the right side because it is longer than 78 characters long

应该是

This one is a very long line that runs off the right side because it is longer 
  than 78 characters long

我找不到解决方案。我注意到我的问题可能重复。我在那里找不到我的答案。我需要能够缩进。

【问题讨论】:

标签: java string


【解决方案1】:

您可以从 String 创建一个 StringBuilder,然后在 78 个字符后的最后一个分词符处插入换行符和制表符。您可以通过获取前 78 个字符的子字符串,然后找到最后一个空格的索引来找到插入换行符/制表符的最后一个单词:

StringBuilder sb = new StringBuilder(Arrays.stream(input.split(" ")).distinct().collect(Collectors.joining(" ")));
if(sb.length() > 78) {
    int lastWordBreak = sb.substring(0, 78).lastIndexOf(" ");        
    sb.insert(lastWordBreak , "\n\t");
}
return sb.toString();

输出:

This one is a very long line that runs off the right side because it longer
     than 78 characters

你的Stream 也没有做你想做的事。是的,它删除了重复的单词,但是.. 它删除了重复的单词。所以对于String

This is a great sentence. It is a great example.

它将删除重复的isgreata,并返回

This is a great sentence. It example.

要仅删除 连续 个重复的单词,您可以查看以下解决方案:

或者,您可以通过将文本拆分为单词来创建自己的元素,并将当前元素与其前面的元素进行比较以删除连续的重复单词

【讨论】:

  • 非常感谢您的帮助。但由于某种原因,您的解决方案似乎正在切断原始字符串中的“是”
  • @Devee 那是因为在您创建的Stream 中,您只允许每个单词中的一个。如果您只想删除连续重复的单词,则必须更改 Stream
【解决方案2】:

而不是使用

Collectors.joining(" ")

可以编写一个自定义收集器,在适当的位置添加新行和缩进。

我们来介绍一个 LineWrapper 类,它包含缩进和限制字段:

public class LineWrapper {

  private final int limit;
  private final String indent;

默认构造函数将字段设置为合理的默认值。 注意缩进是如何以换行符开始的。

  public LineWrapper() {
    limit = 78;
    indent = "\n  ";
  }

自定义构造函数允许客户端指定限制和缩进:

  public LineWrapper(int limit, String indent) {
    if (limit <= 0) {
      throw new IllegalArgumentException("limit");
    }
    if (indent == null || !indent.matches("\\n *")) {
      throw new IllegalArgumentException("indent");
    }
    this.limit = limit;
    this.indent = indent;
  }

Following 是一个正则表达式,用于将输入拆分为一个或多个空格。这样可以确保拆分不会产生空字符串:

private static final String SPACES = " +";

apply 方法将输入拆分并将单词收集成指定最大长度的行,缩进行并删除重复的连续单词。请注意如何使用 Stream.distinct 方法不删除重复项,因为它还会删除不连续的重复项。

public String apply(String input) {
    return Arrays.stream(input.split(SPACES)).collect(toWrappedString());
  }

toWrappedString 方法返回一个收集器,将单词累积在一个新的 ArrayList 中,并使用以下方法:

  • addIfDistinct:将单词添加到 ArrayList
  • combine:合并两个数组列表
  • 换行:分割和缩进行

.

Collector<String, ArrayList<String>, String> toWrappedString() {
    return Collector.of(ArrayList::new, 
                        this::addIfDistinct, 
                        this::combine, 
                        this::wrap);
  }

如果单词与前一个单词不同,则 addIfDistinct 将单词添加到累加器 ArrayList 中。

void addIfDistinct(ArrayList<String> accumulator, String word) {
    if (!accumulator.isEmpty()) {
      String lastWord = accumulator.get(accumulator.size() - 1);
      if (!lastWord.equals(word)) {
        accumulator.add(word);
      }
    } else {
      accumulator.add(word);
    }
  }

combine 方法将第二个 ArrayList 中的所有单词添加到第一个。它还确保第二个 ArrayList 的第一个单词不与第一个 ArrayList 的最后一个单词重复。

ArrayList<String> combine(ArrayList<String> words, 
                          ArrayList<String> moreWords) {
    List<String> other = moreWords;
    if (!words.isEmpty() && !other.isEmpty()) {
      String lastWord = words.get(words.size() - 1);
      if (lastWord.equals(other.get(0))) {
        other = other.subList(1, other.size());
      }
    }
    words.addAll(other);
    return words;
  }

最后,wrap 方法将所有单词附加到一个 StringBuffer 中,当达到行长限制时插入缩进:

String wrap(ArrayList<String> words) {
    StringBuilder result = new StringBuilder();

    if (!words.isEmpty()) {
      String firstWord = words.get(0);
      result.append(firstWord);
      int lineLength = firstWord.length();

      for (String word : words.subList(1, words.size())) {
        //add 1 to the word length,
        //to account for the space character
        int len = word.length() + 1;
        if (lineLength + len <= limit) {
          result.append(' ');
          result.append(word);
          lineLength += len;
        } else {
          result.append(indent);
          result.append(word);
          //subtract 1 from the indent length,
          //because the new line does not count
          lineLength = indent.length() - 1 + word.length();
        }
      }
    }

    return result.toString();
  }

【讨论】: