问题 Java正则表达式 - 擦除字符后跟\ b(退格)


我有一个由用户键盘类型构造的字符串,因此它可能包含 '\b' 字符(退格)。

我想清理字符串,以便它不会包含 '\b' 字符,以及它们要删除的字符。例如,字符串:

String str = "\bHellow\b world!!!\b\b\b.";

应打印为:

Hello world.

我用replaceAll尝试过一些东西,我现在拥有的是:

System.out.println(str.replaceAll("^\b+|.\b+", ""));

哪个印刷品:

你好,世界!!。

'\b' 处理正常,但忽略它的倍数。

那么,我可以用Java的正则表达式来解决它吗?

编辑:

我见过 这个 回答,但它似乎不适用于java的replaceAll。
也许我错过了逐字字符串的东西......


3081
2018-05-11 16:49


起源

如何打印此字符串以获取 \b 在输出? - anubhava
Eclipse的控制台......但我也在XML-RPC中传递它,因为它是一个无效的XML字符而失败了... - Elist
我只是看到了 Hellow world!!!. 在我的Eclipse控制台上没有 \b 展示 - anubhava
也许您需要将控制台编码设置为UTF8。无论如何,您可以调试和检查字符串。 - Elist
看起来你很匹配 A(n)B(n) 字符串,所以 这里 是一个相关的问题,有一个很长的答案解释该怎么做。 - dasblinkenlight


答案:


它不能在一次通过中完成,除非对连续退格的数量有实际限制(没有),并且有一个保证(没有)没有“额外”退格其中没有要删除的前一个字符。

这样做(它只有2条小线):

while (str.contains("\b"))
    str = str.replaceAll("^\b+|[^\b]\b", "");

这处理输入的边缘情况 "x\b\by" 它在开始时有一个额外的退格,一旦第一个退出消耗,应该修剪 x只留下 "y"


5
2018-05-11 17:07



谢谢,我会采用这种方法。将接受这个答案(如果没有其他人有魔法纯正的正则表达式来击败这个......) - Elist
它只需要一个简单的修复。如果输入是 \bbbbbHellow\b world!!!\b\b\b. 它会有结果 Hello world!!. 我认为不是预期的,应该给我们一个输出 bbbbHello world!!. 代替。只需删除开头的量词或制作 \b 一个小组。 - Garis M Suero
@GarisMSuero - 您的示例打印 bbbbHello world. 正如所料。 - Elist
@GarisMSuero我不确定你的意思。你的例子产生了 bbbbHello world. 这似乎是有效的 ideone.com/SlCuW7 - Pshemo
@Pshemo @elist抱歉我的困惑。我仍然不知道量词的用途是什么 + 在这种情况下。 - Garis M Suero


你试图解决的问题无法解决  正则表达式。问题在于语法,它产生语言 {any_symbol}*{any_symbol}^n{\b}^n (这是您输入的特例)不是 定期。你需要在某个地方存储状态(之前有多少符号) \b 和 \b 它已阅读),但DFA无法做到(因为DFA无法知道它能找到多少顺序\ b)。所有提议的解决方案都只适用于您的情况("\bHellow\b world!!!\b\b\b.")并且可以通过更复杂的测试轻松破解。

最简单的解决方案是替换周期对{除了\ b}之外的所有{\ b}

UPD: 解决方案,由提出 @Bohemian 似乎完全正确:

UPD 2: 好像java的正则表达式可以解析 不仅是常规语言,还有像 {a}^n{b}^n 使用递归前瞻,所以在java的情况下,可以将这些组与单个正则表达式匹配。 感谢@Pshemo评论和@Elist编辑!


4
2018-05-11 17:03



我怀疑这可以用正则表达式来完成,但是这个正则表达式将是非常难以理解的,所以创建我们自己的解析器会更好。 - Pshemo
我再次引用编辑中提到的C#示例: stackoverflow.com/a/16604714/1609201。 Java中有类似的吗?如果没有,这两种语言之间的正则表达式功能有何不同? - Elist
现在即使使用这个Java正则表达式: stackoverflow.com/questions/3644266/... 这大概是一个^ n b ^ n? - Pshemo
似乎我不知道所有java正则表达式的可能性。看起来java的正则表达式不仅可以解析常规语言,而且可以匹配带有递归前瞻的输入,所以答案是“它是可能的”(但要注意大输入的StackOverflowError) - qwwdfsad


这看起来像是一份工作

Stack<Character> stack = new Stack<Character>();

// for-each character in the string
for (int i = 0; i < str.length(); i++) {
    char c = str.charAt(i);

    // push if it's not a backspace
    if (c != '\b') {
        stack.push(c);
    // else pop if possible
    } else if (!stack.empty()) {
        stack.pop();
    }
}

// convert stack to string
StringBuilder builder = new StringBuilder(stack.size());

for (Character c : stack) {
    builder.append(c);
}

// print it
System.out.println(builder.toString());

正则表达式虽然不错,但并不适合所有任务。这种方法并不简洁 波西米亚的,但效率更高。在每种情况下使用堆栈都是O(n),而像Bohemian这样的正则表达式是O(n2)在最坏的情况下。


4
2018-05-11 17:31



显然,堆栈是这里的终极解决方案,但我正在寻找一种快速且“内联”的方法来解决这个问题。我也学到了一些不错的正则表达式技巧...... - Elist
@Luke和Elist,我喜欢这个解决方案,但是当“让事情变得有效”时,几乎没有代码可以用正则表达式做什么真的很棒 - 这是一项值得学习的技能。并且它也表现良好 - 确保不是快速纳秒,而是典型的呼叫 replaceAll() 只需几微秒;它“足够快”,如果您需要从应用程序中挤出更多性能,您可以快速继续执行其余代码并稍后重新访问它。 - Bohemian♦


如果我正确理解了这个问题,这就是你问题的解决方案:

String str = "\bHellow\b world!!!\b\b\b.";
System.out.println(str.replace(".?\\\b", ""));

0
2018-05-11 16:55



这不起作用...... - Elist
你没有。他希望模拟每个上面的退格删除键 /b 找到。 - Garis M Suero


这是一个很好的谜语。我认为你可以使用正则表达式 删除相同数量的相同重复字符和 \b小号 (即对于您的特定输入字符串):

String str = "\bHellow\b world!!!\b\b\b.";
System.out.println(str.replaceAll("^\b+|(?:([^\b])(?=\\1*+(\\2?+\b)))+\\2", ""));

这是一个改编 我们怎样才能将^ n b ^ n与Java正则表达式匹配?

看到 IDEONE演示,我添加了 .replace("\b","<B>")); 看看有没有 \b离开了。

输出:

Hello world.

一般的正则表达式解决方案不在正则表达式范围内......现在。


0
2018-05-11 16:56



String本身包含'。​​',模式不包含'。' - Elist
是的,这就是我从模式中删除它的原因。 - Wiktor Stribiżew
有趣,但仍打印Hellow \ b世界。在我的控制台中 - Elist
尝试改变 ! 在输入字符串中 x 看看会发生什么。 (-1) - Bohemian♦
@Bohemian:我编辑了答案,修改了“a ^ n b ^ n”正则表达式。 - Wiktor Stribiżew