Android/Java 正则表达式从子字符串中删除多余的零

Posted

技术标签:

【中文标题】Android/Java 正则表达式从子字符串中删除多余的零【英文标题】:Android/Java Regex to remove extra zeros from sub-strings 【发布时间】:2016-05-01 08:32:09 【问题描述】:

我有以下字符串作为输入:

"2.0,3.00,-4.0,0.00,-0.00,0.03,2.01,0.001,-0.03,101"

最终输出会是这样的:

"2,3,-4,0,0,.03,2.01,.001,-.03,101"

所有前导零和尾随零都将被删除,正/负零都将简单地为零。

我们可以通过首先拆分字符串并为每个部分使用正则表达式来实现这一点。但是我的字符串大小超过 10000。我们如何使用Regex 来实现这一点?

编辑:

答案分析:

我已经用字符串 "0.00,-0.00,00.00,-00.00,40.00,-40.00,4.0,-4.0,4.01,-4.01,04.01,-04.01,004.04,-004.04,0004.040,-0004.040,101,.40,-.40,0.40,-0.40" 测试了所有答案,并且来自 Wiktor Stribiżew 的答案通过了所有测试用例。(请参见此处:https://regex101.com/r/tS8hE3/9)其他答案已通过在大多数情况下,但不是全部。

【问题讨论】:

我只拆分了字符串...然后分别对每个部分使用正则表达式。但这对于大字符串来说效率不高。我怎样才能在不拆分的情况下实现这一目标? 如果它适合你,你试试这个解决方案怎么样***.com/questions/5965767/… 逐个处理字符并将它们收集到StringBuilder 将比正则表达式执行得更快,更节省空间 修复后得到你的输出regex101.com/r/rQ2rG5/1。只是好奇,既然你给了 stribnetz 所有的金子,有没有理由相信你的输入都是有效的数字?您可以解析文本以将其转换为数字。如果它无效,它将引发异常。否则,这是徒劳的练习,即。 如果不是数字,为什么要从数字中删除零?如果您不这样做,则必须在解析时进行验证,这就是我所做的。以这个为例,看看会发生什么regex101.com/r/aH6gX0/1 【参考方案1】:

更新以涵盖更多案例,例如01..10001.10

(?<=,|^)(?:[0.+-]+(?=0(?:,|\.\B|$))|0+(?=[1-9]))|\.0+\b|\b0+(?=\d*\.\b)|\.\B|(?<=[1-9])0+(?=,|$)

这种模式需要更多的回溯,因此在大输入时会变慢。 Java 字符串:

"(?<=,|^)(?:[0.+-]+(?=0(?:,|\\.\\B|$))|0+(?=[1-9]))|\\.0+\\b|\\b0+(?=\\d*\\.\\b)|\\.\\B|(?<=[1-9])0+(?=,|$)"

除了上一个模式之外,这个匹配

(?&lt;=,|^)(?:...|0+(?=[1-9]))[1-9] 之前添加前导零 \.0+\b 修改为仅在 单词边界 之前匹配带有零的句点 \b0+(?=\d*\.\b) 如果句点前面有可选数字,则在边界处匹配零 ahead \.\B 匹配与非单词边界接壤的句点(例如.,(?&lt;=[1-9])0+(?=,|$) 匹配 [1-9] 后面的尾随零

Demo at regex101 或 Regexplanet(点击 Java)


更新前回答 你也可以试试replaceAll这个带空的正则表达式。

(?<=,|^)[0.+-]+(?=0(?:,|$))|\.0+\b|\b0+(?=\.)

(?&lt;=,|^)[0.+-]+(?=0(?:,|$)) 匹配所有仅包含 [0.+-] 且至少尾随零的部分。受限于lookaround assertions:(?&lt;=,|^)(?=0(?:,|$))的使用

|\.0+\b 或匹配一个句点后跟一个或多个零和一个word boundary。

|\b0+(?=\.) 或如果句点为ahead,则匹配后跟一个或多个零的边界。

0.,01,1.10 等未受质疑的情况尚未包含在此模式中。作为 Java 字符串:

"(?<=,|^)[0.+-]+(?=0(?:,|$))|\\.0+\\b|\\b0+(?=\\.)"

Demo at regex101 或 Regexplanet(点击 Java)

【讨论】:

是的..有些部分不包括在内。我们需要删除所有不需要的零 抱歉回复晚了。对于小数点前有两个以上零的情况,您的案例失败,例如:-004.04,此值保持不变 @nKaushik 我明白了,几乎没有修改。有other cases, that my answer treats different 到selected answer。【参考方案2】:

更新了测试用例答案

使用以下正则表达式:

String rx = "-?0+\\.(0)+\\b|\\.0+\\b|\\b0+(?=\\.\\d*[1-9])|\\b0+(?=[1-9]\\d*\\.)|(\\.\\d*?)0+\\b";

并替换为$1$2。见another demo。

正则表达式匹配多个替代项并捕获字符串的某些部分,以便稍后在替换期间重新插入:

-?0+\.(0)+\b - 匹配一个可选的-,后跟一个或多个0s,后跟一个.,然后捕获恰好一个0,但匹配一个或多个匹配项(因为@ 987654331@ 放在0 上,+ 应用于该组);最后的单词边界要求在最后一个匹配的0 之后出现一个非单词字符。在替换中,我们使用$1 反向引用恢复0。因此,-00.0000.00 将被替换为 0| - 或者... \.0+\b - 在, 之前有一个点和一个或多个零(因为字符串是逗号分隔的)。 | - 或者... \b0+(?=\.\d*[1-9]) - 单词边界(字符串的开头或, 之后的位置)后跟一个或多个0s,后跟. + 零个或多个数字后跟一个非0 数字(所以我们删除仅由零组成的整数部分中的前导零) | - 或者... \b0+(?=[1-9]\d*\.) - 一个字边界,后跟一个或多个零,然后是 . 之前的非 0 数字(因此,我们从不等于 0 的整数部分删除所有前导零)。 | - 或者... (\.\d*?)0+\b - 捕获.+零个或多个数字,但尽可能少,直到第一个0,然后只匹配一个或多个零(直到字符串结尾或,)(所以,我们去掉了小数部分的尾随零)

在测试用例更新之前回答

我建议使用一个非常简单且简短的正则表达式来满足您的需求:

-0+\.(0)+\b|\.0+\b|\b0+(?=\.\d*[1-9])

替换为$1

请参阅regex demo。短IDEONE demo:

String re = "-0+\\.(0)+\\b|\\.0+\\b|\\b0+(?=\\.\\d*[1-9])"; 
String str = "2.0,3.00,-4.0,0.00,-0.00,0.03,2.01,0.001,-0.03,101,0.001,-0.03";
String expected = "2,3,-4,0,0,.03,2.01,.001,-.03,101,.001,-.03"; 
System.out.println(str.replaceAll(re, "$1").equals(expected)); // TRUE

解释

-0+\.(0)+\b - 一个减号后跟一个或多个 0s (0+) 后跟一个文字点 (\.) 后跟一个或多个零(并仅捕获与 (0)+ 匹配的最后一个 0 ) 后跟一个单词边界(在此上下文中位于 , 之前的位置) | - 或者... \.0+\b - 一个文字点 (\.),后跟一个或多个零,后跟一个单词边界(在此上下文中位于 , 之前) | - 或者... \b0+(?=\.\d*[1-9]) - 单词边界(在此上下文中位于 , 之后)后跟一个或多个零,必须跟一个文字点 (\.),然后是零个或多个数字和然后是从 1 到 9 范围内的数字(因此小数部分大于0)。

【讨论】:

@nKaushik:我想知道为什么您选择了一个不符合要求的解决方案作为已接受的解决方案。请检查我的答案。 @Wktor :我可以更改已接受的答案 :),奖励仍然开放。请用“.50”检查您的模式 我明白了,你有更多的测试用例。所以,更新是-0+\.(0)+\b|\.0+\b|\b0+(?=\.\d*[1-9])|\b0+(?=[1-9]\d*\.)|(\.\d*?)0+\b 替换为$1$2 你有时间检查吗?我看到很多人同时提出了他们的建议:) 我还为每个交替分支添加了解释。如果有任何不清楚的地方,请发表评论。谢谢你的一个很好的谜语:)【参考方案3】:

/(?!-)(?!0)[1-9][0-9]*\.?[0-9]*[1-9](?!0)|(?!-)(?!0)\.?[0-9]*[1-9](?!0)/g

【讨论】:

【参考方案4】:

使用您问题中的数字列表以及一些其他数字,以下正则表达式替换将删除所有前导零和尾随零。

numbers.replaceAll("\\b0*([1-9]*[0-9]+)(\\.[0-9]*[1-9])?\\.?0*\\b", "$1$2");

有输入:

2.0,3.00,-4.0,0.00,-0.00,0.03,2.01,0.001,-0.03,101,101.1010,0020.00

结果是:

2,3,-4,0,-0,0.03,2.01,0.001,-0.03,101,101.101,20

如果你想要小数不带前导 0,那么你可以使用以下。

numbers.replaceAll("\\b0*([0-9]+)(\\.[0-9]*[1-9])?\\.?0+\\b|0+(\\.[0-9]+?)0*\\b", "$1$2$3");

有输入:

2.0,3.00,-4.0,0.00,-0.00,0.03,2.01,0.001,-0.03,101,101.1010,0020.00

结果是:

2,3,-4,0,-0,.03,2.01,.001,-.03,101,101.101,20

【讨论】:

【参考方案5】:

是否可以只使用替换?示例:

str.replaceAll("\.0+,|,0+(?=\.)", ",");

demo

【讨论】:

【参考方案6】:
\.0+$|^(-)?0+(?=\.)

你可以试试这个。替换为$1。如果你得到空字符串或-替换后替换为0。查看演示。

https://regex101.com/r/cZ0sD2/7

如果你想使用完整的字符串

-?0*\.0+\b|\.0+(?=,|$)|(?:^|(?<=,))(-)?0+(?=\.)

查看演示。

https://regex101.com/r/cZ0sD2/16

【讨论】:

好的..可以使用..但我必须将字符串拆分为子字符串并分别用于所有子字符串。取而代之的是,我如何直接检查逗号之间的字符串并进行修改? 测试字符串应该是“2.0,3.00,-4.0,0.00,-0.00,0.03,2.01,0.001,-0.03,101”,而不是单个元素。 几乎是正确的,但我将不得不再遍历一次字符串来替换 - 用 0。有什么选择吗? 是的..但是“-”和空格?我们是否必须再遍历一次字符串才能将其替换为 0 ? 抱歉删除已接受。你的答案不适用于 .50【参考方案7】:

您可以通过 2 次更换来做到这一点:

首先使用\.0+(?=(,|$))并替换为""

然后使用(?!(^|,))-0(?=(,|$))并将其替换为"0"

【讨论】:

0.001,-0.03 保持不变

以上是关于Android/Java 正则表达式从子字符串中删除多余的零的主要内容,如果未能解决你的问题,请参考以下文章

用于从子资源列表中更新/添加/删除项目的 REST 设计

是否可以在 azcopy 中使用正则表达式?

正则并不适合严格查找子串

正则表达式

4-19 Linux中的正则表达式 --- 字符匹配

php正则表达式是啥?