如何反转具有换行符(\ n或\ r)的字符串中的单词?

时间:2018-07-25 14:16:35

标签: java

我有一个字符串如下:

String sentence = "I have bananas\r" +
                  "He has apples\r"  +
                  "I own 3 cars\n"   +
                  "*!"

我想反转此字符串,以产生如下输出:

                 "*!" +
                 "\ncars 3 own I" +
                 "\rapples has He" +
                 "\rbananas have I" 

这是我编写的程序。

public static String reverseWords(String sentence) {
    StringBuilder str = new StringBuilder();
    String[] arr = sentence.split(" ");
    for (int i = arr.length -1; i>=0; i--){
        str.append(arr[i]).append(" ");

    }
    return str.toString();

}

但是我没有得到预期的输出。怎么了?

3 个答案:

答案 0 :(得分:3)

问题在于您仅在空格上分割,但这不是句子中唯一的空白类型。您可以使用模式\s来匹配所有空格。但是,这样一来,您便不知道拆分后要放回该位置的内容。因此,我们将在空白字符前面或后面的零宽度位置拆分。

将拆分更改为此:

    String[] arr = sentence.split("(?<=\\s)|(?=\\s)");

此外,既然您要保留空白字符,则不再需要附加它们。因此,将您的附加内容更改为此:

        str.append(arr[i]);

最后一个问题是,由于\r的存在,您的输出将出现乱码。因此,如果您想清楚地看到结果,则应替换这些字符。例如:

System.out.println(reverseWords(sentence).replaceAll("\\r","\\\\r").replaceAll("\\n","\\\\n"));

此修改后的代码现在可以提供所需的输出。

输出:

  

*!\ ncars 3个自己的I \ rapples有He \ rbananas的I

注意:

由于您是随意混合\r\n的,所以我没有添加任何代码来将\r\n视为特例,这意味着它将被反转为{{1 }}。如果这是一个问题,那么您将需要防止或撤消这种逆转。

例如,此稍微更复杂的正则表达式将阻止我们反转任何连续的空格字符:

\n\r

上面的正则表达式将匹配零宽度位置,该位置后面有空格但前面没有空格,或者前面有空格但后面没有空格。因此它不会在连续的空格中间分裂,并且将保留序列 String[] arr = sentence.split("(?<=\\s)(?!\\s)|(?<!\\s)(?=\\s)"); 之类的顺序。

答案 1 :(得分:1)

仔细查看所需的输出。实际上,您实际上需要两个迭代步骤-首先需要向后遍历所有,然后向后遍历每行中的所有 words 。目前,您只需要按空格(而不是换行)进行一次拆分,然后遍历该向后返回的所有内容,这将无法满足您的要求!

看看下面的例子-我与您的风格保持紧密联系,只是添加了第二个循环。它首先遍历新行(由于split()使用正则表达式,因此要通过\ n或\ r进行迭代),然后依次通过每行中的单词进行迭代。

但是请注意,这带有警告-它不会保留\r\n。为此,您需要在拆分中使用lookahead / lookbehind来保留定界符(有关示例,请参见here。)

public static String reverseWords(String sentence) {
    StringBuilder str = new StringBuilder();
    String[] lines = sentence.split("[\n\r]");
    for (int i = lines.length - 1; i >= 0; i--) {
        String[] words = lines[i].split(" ");
        for (int j = words.length - 1; j >= 0; j--) {
            str.append(words[j]).append(" ");
        }
        str.append("\n");
    }
    return str.toString();
}

答案 2 :(得分:1)

此问题背后的逻辑很简单,有两个步骤可以达到OP的目标:

  1. 反转整个字符串;
  2. 反转之间的单词(由spaces分隔的单词);

我希望使用StringBuilder来完成此操作,这很容易理解,而不是使用char[]


本地测试代码为:

public class WordReverse {
    public static void main(String... args) {
        String s = " We have bananas\r" +
                "He has apples\r"  +
                "I own 3 cars\n"   +
                "*!";
        System.out.println(reverseSentenceThenWord(s));
    }

    /**
     * return itself if the @param s is null or empty;
     * @param s
     * @return the words (non-whitespace character compound) reversed string;
     */
    private static String reverseSentenceThenWord(String s) {
        if (s == null || s.length() == 0) return s;
        char[] arr = s.toCharArray();
        int len = arr.length;
        reverse(arr, 0, len - 1);
        boolean inWord = !isSpace(arr[0]); // used to track the start and end of a word;
        int start = inWord ? 0 : -1; // is the start valid?
        for (int i = 0; i < len; ++i) {
            if (!isSpace(arr[i])) {
                if (!inWord) {
                    inWord = true;
                    start = i; // just set the start index of the new word;
                }
            } else {
                if (inWord) { // from word to space, we do the reverse for the traversed word;
                    reverse(arr, start, i - 1);
                }
                inWord = false;
            }
        }
        if (inWord) reverse(arr, start, len - 1); // reverse the last word if it ends the sentence;
        String ret = new String(arr);
         ret = showWhiteSpaces(ret);
        // uncomment the line above to present all whitespace escape characters;
        return ret;
    }

    private static void reverse(char[] arr, int i, int j) {
        while (i < j) {
            char c = arr[i];
            arr[i] = arr[j];
            arr[j] = c;
            i++;
            j--;
        }
    }

    private static boolean isSpace(char c) {
        return String.valueOf(c).matches("\\s");
    }

    private static String showWhiteSpaces(String s) {
        String[] hidden = {"\t", "\n", "\f", "\r"};
        String[] show = {"\\\\t", "\\\\n", "\\\\f", "\\\\r"};
        for (int i = hidden.length - 1; i >= 0; i--) {
            s = s.replaceAll(hidden[i], show[i]);
        }
        return s;
    }
}

在我的PC中,输出不是,而是提供的OP,而是:

*!
bananas have I

但是,如果您设置一个断点并对其进行调试并检查返回的字符串,则它将为:

enter image description here

这是正确的答案。

更新

现在,如果您想显示escaped空格,则可以取消注释,然后返回结果:

// ret = showWhiteSpaces(ret);

最终输出将与OP的问题完全相同:

*!\ncars 3 own I\rapples has He\rbananas have I