问题描述
小M在工作时遇到了一个问题,他需要将用户输入的不带千分位逗号的数字字符串转换为带千分位逗号的格式,并且保留小数部分。小M还发现,有时候输入的数字字符串前面会有无用的 0
,这些也需要精简掉。请你帮助小M编写程序,完成这个任务。
测试样例
样例1:
输入:
s = "1294512.12412"
输出:'1,294,512.12412'
样例2:
输入:
s = "0000123456789.99"
输出:'123,456,789.99'
样例3:
输入:
s = "987654321"
输出:'987,654,321'
答案
public class Main {
public static String solution(String s) {
// PLEASE DO NOT MODIFY THE FUNCTION SIGNATURE
// write code here
// 去除前面的零
s = s.replaceFirst("^0+(?!$)", "");
// 分离整数部分和小数部分
String[] parts = s.split("\\.");
String integerPart = parts[0];
String decimaPart = parts.length > 1 ? parts[1] : "";
// 在适当位置插入逗号
// 计数器count
int count = 0;
// 用StringBilider对字符串进行动态构建
StringBuilder sb = new StringBuilder();
// 从右往左遍历整数部分
for (int i = integerPart.length() - 1; i >= 0; i--) {
// 从右往左把整数部分添加到sb中
sb.append(integerPart.charAt(i));
count++;
// 当达到三个后添加逗号
if (count % 3 == 0 && i != 0) {
sb.append(",");
}
}
// 将字符串反转
sb.reverse();
// 进行字符串的拼接
return sb.toString() + (decimaPart.isEmpty() ? "" : "." + decimaPart);
}
public static void main(String[] args) {
//样例测试
System.out.println(solution("1294512.12412").equals("1,294,512.12412"));
System.out.println(solution("0000123456789.99").equals("123,456,789.99"));
System.out.println(solution("987654321").equals("987,654,321"));
}
}
解析
1.问题理解
我们需要将一个不带千分位逗号的数字字符串转换为带千分位逗号的格式,并且保留小数部分。此外,输入的数字字符串前面可能会有无用的 0
,这些也需要精简掉。
2.算法步骤
-
去除前导零:首先,我们需要去除输入字符串前面的无用
0
。可以使用String.replaceFirst("^0+(?!$)", "")
来实现这一点。 -
分离整数部分和小数部分:接下来,我们需要将字符串分为整数部分和小数部分。可以使用
String.split("\\.")
来实现这一点。 -
处理整数部分:对于整数部分,我们需要从右往左每三位插入一个逗号。可以使用
StringBuilder
来构建结果字符串,并在适当的位置插入逗号。 -
合并结果:最后,将处理后的整数部分和小数部分(如果有)合并成最终的结果字符串。
3.代码分段解析
去除前面的零
// 去除前面的零
s = s.replaceFirst("^0+(?!$)", "");
-
String.replaceFirst
方法:replaceFirst
方法用于替换字符串中第一个匹配正则表达式的子字符串。- 在这里,
replaceFirst("^0+(?!$)", "")
会将字符串开头的一个或多个0
替换为空字符串,从而去除这些前导零。
-
**正则表达式
^0+(?!$)
**:^
:表示字符串的开始。0+
:表示一个或多个0
。(?!$)
:这是一个负向前瞻断言,表示匹配的0
后面不能是字符串的结尾。
这个正则表达式的意思是:匹配字符串开头的一个或多个
0
,但这些0
不能是字符串的结尾。 -
正则表达式中的前瞻断言
在正则表达式中,前瞻断言(lookahead assertion)用于检查某个位置之后是否存在某种模式,而不实际匹配该模式。前瞻断言有两种类型:
正向前瞻断言:(?=...)
,表示匹配位置之后必须存在某种模式。
负向前瞻断言:(?!...)
,表示匹配位置之后不能存在某种模式。(?!$)
的解释:(?!...)
:这是一个负向前瞻断言。
$
:表示字符串的结尾。
因此,(?!$)
表示匹配的位置之后不能是字符串的结尾。
示例
假设我们有以下输入字符串:
在"0000123456789.99"
^0+
会匹配字符串开头的所有0
,即"0000"
。(?!$)
会检查这些0
后面是否不是字符串的结尾。"0000123456789.99"
中,0000
后面是1
,不是字符串的结尾,因此匹配成功。
这样就有效避免了输入0但是把它变为空字符串的结果 -
正则表达式中的
^
符号在正则表达式中,
在字符集(character class)中:^
符号有两种主要用途:
例如[^0-9]
,表示匹配任何不是数字的字符。
在字符集之外:
例如^0+
,表示匹配字符串的开始位置。
分离整数和小数部分
// 分离整数部分和小数部分
String[] parts = s.split("\\.");
String integerPart = parts[0];
String decimaPart = parts.length > 1 ? parts[1] : "";
-
字符串分割
在Java中,String.split
方法用于将字符串根据指定的分隔符分割成多个子字符串,并返回一个字符串数组。 -
String.split("\\.")
的解释split
方法的参数是一个正则表达式。\\.
:表示匹配一个点(.
)。在正则表达式中,.
是一个特殊字符,表示匹配任意字符。因此,我们需要使用\\
进行转义,使其匹配实际的点字符。 -
示例
假设我们有以下输入字符串:
"123456789.99"
String[] parts = "123456789.99".split("\\.");
parts[0]
将是"123456789"
(整数部分)。parts[1]
将是"99"
(小数部分)。
使用split("\\.")
进行分割:
用StringBilder对字符串进行动态构建
// 用StringBilider对字符串进行动态构建
StringBuilder sb = new StringBuilder();
// 从右往左遍历整数部分
for (int i = integerPart.length() - 1; i >= 0; i--) {
// 从右往左把整数部分添加到sb中
sb.append(integerPart.charAt(i));
count++;
// 当达到三个后添加逗号
if (count % 3 == 0 && i != 0) {
sb.append(",");
}
}
// 将字符串反转
sb.reverse();
- 为什么使用StringBilder ?
StringBuilder
是一个可变的字符串类,非常适合用于字符串的动态构建和修改。我们可以使用StringBuilder
来逐步构建结果字符串,并在适当的位置插入逗号。 -
具体步骤
**初始化StringBuilder
**:创建一个新的StringBuilder
对象。
从右往左遍历整数部分:使用一个循环从整数部分的末尾开始,逐个字符添加到StringBuilder
中。
插入逗号:每添加三个字符后,插入一个逗号。
反转字符串:由于我们是倒序添加字符的,最后需要将StringBuilder
中的字符串反转。
// 合并整数部分和小数部分
return sb.toString() + (decimalPart.isEmpty() ? "" : "." + decimalPart);
-
sb.toString()
:将StringBuilder
对象sb
转换为字符串。 decimalPart.isEmpty()
:检查decimalPart
是否为空字符串。decimalPart
是从输入字符串中分离出来的小数部分。(decimalPart.isEmpty() ? "" : "." + decimalPart)
:这是一个三元运算符,用于根据decimalPart
是否为空来决定是否拼接小数部分。
如果decimalPart
为空,返回空字符串""
。
如果decimalPart
不为空,返回"." + decimalPart
,即在整数部分后面加上小数点和小数部分。