首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> 網頁程式設計 >> PHP 程式設計 >> PHP 相關知識

PHP 人民幣數值轉中文大寫類:多版本完整實作

     編輯:PHP 相關知識

PHP 人民幣數值轉中文大寫類:多版本完整實作

財務系統、合同生成、報表導出常需將阿拉伯數字金額轉為中文大寫(壹貳參肆伍陸柒捌玖)。本文提供三個版本實作:功能完整版、簡潔版、高效版,涵蓋四捨五入、進位、零處理、幣種單位自定義等細節。

版本一:功能完整版(支援所有邊界情況)

class Num2RmbClass {
  /**
   * 人民幣數值轉中文大寫
   * @param string $number      金額字串,預設 '0'
   * @param string $int_unit    整數單位,預設 '元'(可改 '圓')
   * @param bool   $is_round    小數是否四捨五入,預設 true
   * @param bool   $is_extra_zero 整數尾零且有小數時是否輸出『零』,預設 false
   * @param bool   $dec_to_int  小數進位到個位,預設 false
   * @return string 中文大寫金額
   */
  public static function num2rmb($number = '0', $int_unit = '元', $is_round = true, $is_extra_zero = false, $dec_to_int = false) {
    // 1. 分離整數/小數部分
    $parts = explode('.', $number, 2);
    $int = isset($parts[0]) ? strval($parts[0]) : '0';
    $dec = isset($parts[1]) ? strval($parts[1]) : '';
    
    // 2. 小數位處理:超過 2 位按參數決定截斷/四捨五入/進位
    $dec_len = strlen($dec);
    if ($dec_len > 2) {
      if ($is_round) {
        $rounded = round(floatval("0." . $dec), 2);
        if ($rounded == 1 && $dec_to_int) {      // 0.995 → 1.00 進位
          $int = empty($int) ? '1' : strval(intval($parts[0]) + 1);
          $dec = '00';
        } elseif ($rounded == 1) {               // 僅四捨五入不進位
          $dec = '99';
        } else {
          $dec = substr(strrchr(strval($rounded), '.'), 1);
        }
      } else {
        $dec = substr($dec, 0, 2); // 直接截斷
      }
    }
    
    // 3. 零值快速返回
    if (empty($int) && empty($dec)) return '零';
    
    // 4. 定義中文數字與單位
    $chs = array('零','壹','貳','參','肆','伍','陸','柒','捌','玖');
    $uni = array('','拾','佰','仟');
    $dec_uni = array('角','分');
    $exp = array('', '萬');
    $res = '';
    
    // 5. 整數部分:從右向左,每 4 位一段(萬、億)
    for ($i = strlen($int) - 1, $k = 0; $i >= 0; $k++) {
      $str = '';
      for ($j = 0; $j < 4 && $i >= 0; $j++, $i--) {
        $u = $int{$i} > 0 ? $uni[$j] : '';
        $str = $chs[$int{$i}] . $u . $str;
      }
      $str = rtrim($str, '0');
      $str = preg_replace('/0+/', '零', $str);
      if (!isset($exp[$k])) $exp[$k] = $exp[$k-2] . '億';
      $u2 = $str != '' ? $exp[$k] : '';
      $res = $str . $u2 . $res;
    }
    
    // 6. 小數部分:從左向右
    $dec = rtrim($dec, '0');
    if (!empty($dec)) {
      $res .= $int_unit;
      if ($is_extra_zero && substr($int, -1) === '0') $res .= '零';
      for ($i = 0, $cnt = strlen($dec); $i < $cnt; $i++) {
        $u = $dec{$i} > 0 ? $dec_uni[$i] : '';
        $res .= $chs[$dec{$i}] . $u;
      }
      $res = rtrim($res, '0');
      $res = preg_replace('/0+/', '零', $res);
    } else {
      $res .= $int_unit . '整';
    }
    
    return $number < 0 ? '(負)' . $res : $res;
  }
}

// 使用範例
echo Num2RmbClass::num2rmb('1600020039.9989', '圓', false, false, false);
// 輸出:壹拾陸億零貳佰零參拾玖圓玖角玖分

版本二:簡潔陣列映射版(適合快速集成)

function cny($ns) {
  static $cnums = array('零','壹','貳','參','肆','伍','陸','柒','捌','玖');
  static $cnyunits = array('圓','角','分');
  static $grees = array('拾','佰','仟','萬','拾','佰','仟','億');
  
  list($ns1, $ns2) = explode('.', $ns, 2);
  $ns2 = array_filter(array($ns2[1], $ns2[0])); // 分、角
  
  $ret = array_merge($ns2, array(implode('', _cny_map_unit(str_split($ns1), $grees)), ''));
  $ret = implode('', array_reverse(_cny_map_unit($ret, $cnyunits)));
  return str_replace(array_keys($cnums), $cnums, $ret);
}

function _cny_map_unit($list, $units) {
  $ul = count($units);
  $xs = array();
  foreach (array_reverse($list) as $x) {
    $l = count($xs);
    if ($x != '0' || !($l % 4)) {
      $n = ($x == '0' ? '' : $x) . $units[($l-1) % $ul];
    } else {
      $n = is_numeric($xs[0][0]) ? $x : '';
    }
    array_unshift($xs, $n);
  }
  return $xs;
}

echo cny('23058.04'); // 貳萬參仟零伍拾捌圓肆分

版本三:高效字串操作版(無陣列開銷,適合高頻調用)

function num_to_rmb($num) {
  $c1 = "零壹貳參肆伍陸柒捌玖";
  $c2 = "分角元拾佰仟萬拾佰仟億";
  
  $num = round($num, 2) * 100; // 轉為分,整數處理
  if (strlen($num) > 10) return "金額太大,請檢查";
  
  $i = 0; $c = "";
  while (true) {
    $n = ($i == 0) ? substr($num, -1, 1) : $num % 10;
    $p1 = substr($c1, 3 * $n, 3); // 取中文數字
    $p2 = substr($c2, 3 * $i, 3); // 取單位
    
    // 非零或關鍵單位(億/萬/元)才加單位
    if ($n != '0' || ($n == '0' && in_array($p2, array('億','萬','元')))) {
      $c = $p1 . $p2 . $c;
    } else {
      $c = $p1 . $c;
    }
    
    $i++; $num = intval($num / 10);
    if ($num == 0) break;
  }
  
  // 清理多餘『零』:零元→元、零萬→萬、零億→億、零零→零
  $j = 0; $slen = strlen($c);
  while ($j < $slen) {
    $m = substr($c, $j, 6); // UTF-8 一漢字 3 字節
    if (in_array($m, array('零元','零萬','零億','零零'))) {
      $c = substr($c, 0, $j) . substr($c, $j + 3);
      $j -= 3; $slen -= 3;
    }
    $j += 3;
  }
  
  // 尾部『零』處理
  if (substr($c, -3) == '零') $c = substr($c, 0, -3);
  
  return empty($c) ? '零元整' : $c . '整';
}

echo num_to_rmb(23000000.00); // 貳仟參佰萬元整

三版本對比與選型建議

版本特點適用場景效能
完整版參數豐富、邊界全覆蓋、OOP 風格財務核心系統、需精確控制格式中等
簡潔版代碼短、陣列映射巧妙、易閱讀快速原型、中小專案、教學示範良好
高效版字串操作、無陣列開銷、單函數高頻調用、批量導出、效能敏感場景最佳

關鍵測試用例

$tests = array(
  '0'           => '零元整',
  '0.01'        => '零元零壹分',
  '0.10'        => '零元壹角整',
  '100'         => '壹佰元整',
  '101'         => '壹佰零壹元整',
  '1000'        => '壹仟元整',
  '1001'        => '壹仟零壹元整',
  '10000'       => '壹萬元整',
  '10001'       => '壹萬零壹元整',
  '100000000'   => '壹億元整',
  '123456789.45'=> '壹億貳仟參佰肆拾伍萬陸仟柒佰捌拾玖元肆角伍分',
  '-99.99'      => '(負)玖拾玖元玖角玖分',
);

foreach ($tests as $input => $expected) {
  $out = Num2RmbClass::num2rmb($input);
  echo "$input => $out " . ($out === $expected ? '✓' : '✗ EXPECT: '.$expected) . "\n";
}

財務級建議:生產環境務必建立自動化測試覆蓋上述邊界用例,並對接單元測試流程。涉及金額計算請優先使用 bcmath 任意精度擴展避免浮點誤差,轉換前先 bcadd($num, '0', 2) 固定兩位小數。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved