我有一个字符串如下:
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();
}
但是我没有得到预期的输出。怎么了?
答案 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的目标:
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
但是,如果您设置一个断点并对其进行调试并检查返回的字符串,则它将为:
这是正确的答案。
现在,如果您想显示escaped
空格,则可以取消注释,然后返回结果:
// ret = showWhiteSpaces(ret);
最终输出将与OP的问题完全相同:
*!\ncars 3 own I\rapples has He\rbananas have I