万水千山

Across Mountains and Seas

将阿拉伯数字金额转换为人民币大写形式

Humoon / 2019-10-11


本程序理论上可以转换任意位数,但由于 R 语言储存数据的精度问题,当包含角、分时,只能转换百万亿人民币以下数量级的金额数。

library(tidyverse)
## -- Attaching packages --------------------------------------- tidyverse 1.3.1 --
## v ggplot2 3.3.5     v purrr   0.3.4
## v tibble  3.1.3     v dplyr   1.0.7
## v tidyr   1.1.3     v stringr 1.4.0
## v readr   2.0.0     v forcats 0.5.1
## -- Conflicts ------------------------------------------ tidyverse_conflicts() --
## x dplyr::filter() masks stats::filter()
## x dplyr::lag()    masks stats::lag()
library(data.table)
## 
## 载入程辑包:'data.table'
## The following objects are masked from 'package:dplyr':
## 
##     between, first, last
## The following object is masked from 'package:purrr':
## 
##     transpose
han_list = c("零" , "壹" , "贰" , "叁" , "肆", "伍" , "陆" , "柒" , "捌" , "玖")


# 划分整数和小数部分的函数
divide <- function(num){
  integer <- floor(num)
  fraction <- round((num - integer)*100)
  c(integer, fraction) %>% return()
}


# 输出小数部分的函数
fraction_to_rmb <- function(fraction){
  a <- fraction %/% 10
  b <- fraction %% 10
  if (a^2 + b^2 == 0) {
    return('整')
  } else if (a == 0 & b != 0) {
    return(str_c('零', han_list[b + 1], '分'))
  } else if (a != 0 & b == 0) {
    return(str_c(han_list[a + 1], '角'))
  } else {
    return(str_c(han_list[a + 1], '角', han_list[b + 1], '分'))
  }
}


# 输出整数部分的函数
integer_to_rmb <- function(integer){
  string <- integer %>% as.character()
  n <- str_length(string)
  m <- (n + 7)/8 # 8位一组来操作,拆分成 m 组
  s <- ''
  for (i in 1:m) {
    if (i < m) {
      s[i] <- str_sub(string, -8*i, (7 - 8*i)) %>% 
        eight_to_rmb()
    } else {
      s[i] <- str_sub(string, 1, (7 - 8*m)) %>% 
        eight_to_rmb()
    }
  }
  # 将每组返回的字符串连接起来
  rmb <- ''
  for (i in m:1) {
    rmb <- str_c(rmb, s[i])
  }
  # 用正则表达式处理连续的多个零
  rmb <- rmb %>% str_replace_all('零+','零') %>% # 连续多个零变成一个
    str_replace_all('零万', '万') %>% # 万和亿前的末位零删掉
    str_replace_all('零亿', '亿') %>% 
    str_replace('^壹什', '什') %>% # 开头不会读作“壹什”
    str_sub(1, -2) # 去掉个位的“亿”
  return(rmb)
}


## 处理8位数字的函数
eight_to_rmb <- function(string){
  n <- str_length(string)
  if (n == 8) { # 8位满时
    front <- str_sub(string, 1, 4) #前4位
    behind <- str_sub(string, 5, 8) # 后4位
    if (str_sub(string, 1, 8) == '00000000') {
      return('零') # 8位皆0不要“亿”
    } else if (front == '0000') {
      return(str_c('零', four_to_rmb(behind), '亿')) # 4位皆0不要“万”
    } else {
      return(str_c(four_to_rmb(front), '万', four_to_rmb(behind), '亿'))
    }
  } else if (n > 4) { # 不足8位(数字最前的若干位)
    front <- str_sub(string, 1, n - 4)
    behind <- str_sub(string, n - 3, n)
    return(str_c(four_to_rmb(front), '万', four_to_rmb(behind), '亿'))
  } else {# 不足4位
    return(str_c(four_to_rmb(string), '亿'))
  }
}


## 处理4位数字的函数
four_to_rmb <- function(string){
  n <- str_length(string)
  s <- ''
  for (i in 1:n) {
    s[i] <-  str_sub(string, i, i)
  }
  dt <- tibble(seq = n:1, num = s) %>% setDT()
  dt[, han_character := han_list[num %>% as.integer() + 1]] # 中文大写
  dt[, magnitude := ''] # 单位
  dt[seq == 2, magnitude := '什']
  dt[seq == 3, magnitude := '佰']
  dt[seq == 4, magnitude := '仟']
  dt[num == '0', magnitude := ''] # 数字为0时,十百千单位要去掉
  # 组合数字与单位
  dt[, rmb := str_c(han_character, magnitude)]
  return(dt$rmb %>% str_c(collapse = ''))
}


## 主体函数
main <- function(float_money){
  # 首先将金额分为整数部分和小数部分
  integer <- divide(float_money)[1]
  fraction <- divide(float_money)[2]
  # 用两个函数分别处理整数分布和小数部分
  rmb <- str_c(integer_to_rmb(integer), 
               '圆', 
               fraction_to_rmb(fraction))
  cat(rmb)
}


main(202000012345678.09)
## 贰佰零贰万亿壹仟贰佰叁什肆万伍仟陆佰柒什捌圆零玖分
main(202000012345678.00)
## 贰佰零贰万亿壹仟贰佰叁什肆万伍仟陆佰柒什捌圆整
main(202000002305008.09)
## 贰佰零贰万亿零贰佰叁什万伍仟零捌圆零玖分