1. 简介

本文将探讨如何从数字字符串中移除无效零(包括前导零和尾随零)。我们将使用多种实现方式,包括标准Java核心库中的方法。每种实现都会处理正负数字符串的示例。

2. 使用 String.replaceAll()

首先看 String.replaceAll() 方法。该方法通过正则表达式替换字符串内容。我们将调用它来移除字符串中的前导零和尾随零。

核心思路:匹配字符串开头或结尾的所有零,并替换为空字符串。具体分两种情况:

  • 如果字符串包含小数点 → 递归调用处理前导零和尾随零
  • 否则 → 仅处理前导零

以小数为例:

@Test
public void givenPositiveNumberString_whenUsingStringReplaceAll_thenInsignificantZeroRemoved() {
    String positiveNumber = "001200.35000";
    positiveNumber = positiveNumber.contains(".") ? positiveNumber.replaceAll("^(-?)0+(\\d+)", "$1$2")
      .replaceAll("0+$", "")
      .replaceAll("\\.$", ".0") : positiveNumber.replaceAll("^(-?)0+(\\d+)", "$1$2");

    assertEquals("1200.35", positiveNumber);
}
@Test
public void givenNegativeNumberString_whenUsingStringReplaceAll_thenInsignificantZeroRemoved() {
    String negativeNumber = "-0015.052200";
    negativeNumber = negativeNumber.contains(".") ? negativeNumber.replaceAll("^(-?)0+(\\d+)", "$1$2")
      .replaceAll("0+$", "")
      .replaceAll("\\.$", ".0") : negativeNumber.replaceAll("^(-?)0+(\\d+)", "$1$2");

    assertEquals("-15.0522", negativeNumber);
}

✅ 正则解析:

  • ^(-?)0+(\\d+) → 匹配前导零(保留负号)
  • 0+$ → 匹配尾随零
  • \\.$ → 处理小数点后全为零的情况(替换为".0")

⚠️ 踩坑点:直接替换可能导致"123."变成"123",需特殊处理小数点后无数字的情况。

3. 使用 DecimalFormat

DecimalFormat 类允许通过模式自定义数字格式。但有个限制:它会根据模式截断小数位数

@Test
public void givenPositiveNumberString_whenUsingDecimalFormat_thenInsignificantZeroRemoved() {
    String positiveNumber = "001200.35000";
    positiveNumber = new DecimalFormat("0.0#####").format(Double.valueOf(positiveNumber));

    assertEquals("1200.35", positiveNumber);
}
@Test
public void givenNegativeNumberString_whenUsingDecimalFormat_thenInsignificantZeroRemoved() {
    String negativeNumber = "-0015.052200";
    negativeNumber = new DecimalFormat("0.0#####").format(Double.valueOf(negativeNumber));

    assertEquals("-15.0522", negativeNumber);
}

✅ 模式解析:

  • "0.0####"
    • 0:强制显示该位(即使为零)
    • #:仅当数字非零时显示
    • 五个#表示最多保留5位小数

❌ 局限性:

  • 需预设最大小数位数
  • 转换过程涉及Double,可能丢失精度(如超大数字)

4. 使用 BigDecimal

BigDecimal 提供了更专业的处理方式。初始化时自动移除前导零,并通过 stripTrailingZeros() 移除尾随零。注意:当小数部分全为零时,会直接省略小数点

@Test
public void givenPositiveNumberString_whenUsingBigDecimal_thenInsignificantZeroRemoved() {
    String positiveNumber = "001200.35000";
    positiveNumber = new BigDecimal(positiveNumber).stripTrailingZeros()
      .toPlainString();

    assertEquals("1200.35", positiveNumber);
}
@Test
public void givenNegativeNumberString_whenUsingBigDecimal_thenInsignificantZeroRemoved() {
    String negativeNumber = "-0015.052200";
    negativeNumber = new BigDecimal(negativeNumber).stripTrailingZeros()
      .toPlainString();

    assertEquals("-15.0522", negativeNumber);
}

✅ 优势:

  • 精确处理超大/超小数字
  • 自动处理科学计数法问题
  • 无需预设小数位数

⚠️ 特殊情况:

  • 输入"123.000" → 输出"123"(小数点被移除)
  • 输入"0.000" → 输出"0"

5. 总结

本文对比了三种移除无效零的方法:

方法 优点 缺点
replaceAll() 无需类型转换,纯字符串操作 正则复杂,边界情况处理繁琐
DecimalFormat 代码简洁 精度丢失,需预设小数位数
BigDecimal 精确可靠,自动处理各种情况 性能稍低(对象创建开销)

💡 选择建议:

  • 需要高性能且数字范围可控 → DecimalFormat
  • 处理金融/超大数字 → BigDecimal
  • 简单场景且不想引入额外依赖 → replaceAll()