这几天项目要实现一个播报类似于“XXX到账多少元”的功能,要我搞一个工具jar包。度娘上找了一些资料,定下一个大概思路:先将数字金额转换成中文金额,然后对其进行播报(最后效果感觉不如直接对数字进行播报,有兴趣的可以自己尝试一下,这个功能也可以用在其他地方)。
本文主要记录一下过程及最后结果,相关代码和链接也会填出来。
首先是中文金额转换,参考链接:https://github.com/zhao520a1a/NumToCN
原文中else if((numIndex - 2) % 4 == 0 && number % 1000 != 0 ) { //每隔4位,打印“圆,万,亿”
这里导致50000000输出为伍仟元,所以做了一点修改。
import java.math.BigDecimal;
public class NumToRMB {
private static StringBuilder sb = new StringBuilder();
private static final String[] CN_NUMBER = {"零", "壹", "贰", "叁", "肆", "伍", "六", "柒", "捌", "玖"};
private static final String[] CN_UNIT = { "分", "角", "圆","拾", "佰", "仟", "万", "拾", "佰", "仟", "亿", "拾", "佰", "仟", "兆", "拾", "佰", "仟", "顺" };
private static final String CN_NEGATIVE = "负";
private static final String CN_ZERO_FULL = "零圆";
private static final int PERCISION = 2; // 精度
public static String numToCn (BigDecimal numOfMoney) {
//当此 BigDecimal 的值为负、零或正时,返回 -1、0 或 1。
int signum = numOfMoney.signum();
//若输入为0,输出零圆整;
if(signum == 0) {
return CN_ZERO_FULL;
}
//对金额进行四舍五入转化为long类型的整数;先将数的小数点向右移两位,然后在四舍五入,取绝对值,最后将它转换为长整型;
long number = numOfMoney.movePointRight(PERCISION).setScale(0, BigDecimal.ROUND_HALF_UP).abs().longValue();
int numIndex = 0; //记录数字的个数;
boolean getZero = false;
//得到小数部分(小数点后两位);
long scale = number % 100;
if(scale == 0) { //若小数部分为"00"时的情况;
numIndex += 2;
getZero = true;
number /= 100; // 从number去掉为0数;
} else if(scale % 10 == 0){ //若小数部分为"*0"时的情况;
numIndex += 1;
getZero = true;
number /= 10;// 从number去掉为0数;
}
//排除上述两种小数部分的特殊情况,则对小数和整数的处理就是一样的了
while(true) {
//循环结束条件;
if(number <= 0){
break;
}
//每次通过取余来得到最后一位数;
int numUnit = (int)(number % 10);
if(numUnit != 0){
sb.insert(0, CN_UNIT[numIndex]); //先添加单位
sb.insert(0, CN_NUMBER[numUnit]); //在添加根据数字值来对应数组中的中文表述;
getZero = false; //表明当前数不是0;
}
else {
//它的上一次的数不是零,那么打印出零;
if(!getZero) {
sb.insert(0, CN_NUMBER[numUnit]);
}
//若角分位为零,那么打印零;
if(numIndex == 2) {
if(number > 0) {
sb.insert(0, CN_UNIT[numIndex]);
}
} else if((numIndex - 2) % 4 == 0 && number / 1000 <= 9 ) { //每隔4位,打印“圆,万,亿”
sb.insert(0, CN_UNIT[numIndex]);
}
getZero = true; //将其置为true,那么如果下一位还是0,也不会再打印一遍'零';
}
// 从number每次都去掉最后一个数
number = number / 10;
numIndex++;
}
// 如果signum == -1,则说明输入的数字为负数,就在最前面追加特殊字符:负
if(signum == -1) {
sb.insert(0, CN_NEGATIVE);
}
return sb.toString();
}
}
语音播报参考:http://www.manongjc.com/article/60219.html
APPID、AK、SK都可在百度云开通相关功能后获取。
import com.baidu.aip.speech.AipSpeech;
import com.baidu.aip.speech.TtsResponse;
import com.baidu.aip.util.Util;
import javazoom.jl.player.Player;
import org.json.JSONObject;
import java.io.BufferedInputStream;
import java.io.FileInputStream;
import java.io.IOException;
import java.util.HashMap;
/**
* 将文本上传至百度服务器,返回语音文件,如output.mp3,并播放MP3文件
* SDK下载地址:https://ai.baidu.com/file/EDF42F3DB3B4489FA1A88FD1E2BC5D74
*/
public class VoiceCompose {
// 设置APPID/AK/SK
// 百度AI开发平台的控制台中创建一个语音应用即可获得
private static final String APP_ID = "25***66";
private static final String API_KEY = "mT******3P";
private static final String SECRET_KEY = "n********j";
private static final AipSpeech aipSpeech = getAipSpeech();
private static String filename = "output.mp3";
private static Player player;
private static AipSpeech getAipSpeech(){
// 初始化一个AipSpeech
AipSpeech aipSpeech = new AipSpeech(APP_ID, API_KEY, SECRET_KEY);
// 可选:设置网络连接参数
aipSpeech.setConnectionTimeoutInMillis(2000);
aipSpeech.setSocketTimeoutInMillis(60000);
// 可选:设置代理服务器地址, http和socket二选一,或者均不设置
//aipSpeech.setHttpProxy("proxy_host", proxy_port); // 设置http代理
//aipSpeech.setSocketProxy("proxy_host", proxy_port); // 设置socket代理
// 可选:设置log4j日志输出格式,若不设置,则使用默认配置
// 也可以直接通过jvm启动参数设置此环境变量
// System.setProperty("aip.log4j.conf", "path/to/your/log4j.properties");
return aipSpeech;
}
/**
* 将文字转为MP3文件,需联网,依靠百度语音合成
* @param text
* @return 是否成功
*/
public boolean getMP3ByText(String text){
player = null;
// 设置可选参数
HashMap<String, Object> options = new HashMap<>();
// 语速,取值0-9,默认为5中语速
options.put("spd", "3.6");
// 音调,取值0-9,默认为5中语调
options.put("pit", "5");
// 音量,取值0-15,默认为5中音量
options.put("vol", "12");
// 发音人选择, 0为女声,1为男声,3为情感合成-度逍遥,4为情感合成-度丫丫,默认为普通女
options.put("per", "0");
// 调用接口
// text 合成的文本,使用UTF-8编码。小于2048个中文字或者英文数字。(文本在百度服务器内转换为GBK后,长度必须小于4096字节)
// lang 固定值zh。语言选择,目前只有中英文混合模式,填写固定值zh
// ctp 客户端类型选择,web端填写固定值1
TtsResponse res = aipSpeech.synthesis(text, "zh", 1, options);
// 如果合成成功,下行数据为二进制语音文件,包含在data中。 如果合成出现错误,则会填充返回值到result中。
// 若请求错误,服务器将返回的JSON文本包含以下参数:
// error_code:错误码。
// error_msg:错误描述信息,帮助理解和解决发生的错误。
byte[] data = res.getData();
JSONObject res1 = res.getResult();
if (data != null) {
try {
Util.writeBytesToFileSystem(data, filename);
} catch (IOException e) {
e.printStackTrace();
return false;
}
}
if (res1 != null) {
System.out.println(res1.toString(2));
}
return true;
}
public void playMP3(){
try {
BufferedInputStream buffer = new BufferedInputStream(new FileInputStream(filename));
// 需导入javazoom.jl.player.Player,下载地址http://www.javazoom.net/javalayer/sources/jlayer1.0.1.zip
player = new Player(buffer);
player.play();
} catch (Exception e) {
System.out.println(e);
}
}
public String playerStatus(){
if(player == null){
return "null";
}else if(player.isComplete()){
return "played";
}else{
return "playing";
}
}
}
再附上Maven依赖:
<dependencies>
<dependency>
<groupId>com.baidu.aip</groupId>
<artifactId>java-sdk</artifactId>
<version>4.12.0</version>
</dependency>
<dependency>
<groupId>javazoom</groupId>
<artifactId>jlayer</artifactId>
<version>1.0.1</version>
</dependency>
</dependencies>
测试:
public class VoiceTest {
public static void main(String[] args) {
Scanner scan = new Scanner(System.in);
double money = scan.nextDouble();
BigDecimal numOfMoney = new BigDecimal(money);
String s = NumToRMB.numToCn(numOfMoney);
System.out.println(s);
VoiceCompose voiceCompose = new VoiceCompose();
String text = "系统到账" + s;
if(!voiceCompose.getMP3ByText(text)){
System.out.println("转换失败");
}else{
voiceCompose.playMP3();
}
}
}