将英文字符串转换为数字
1. 需求
近期项目中遇到了一个需求,需要将英文字符串转换为阿拉伯数字。在stackoverflow上和csdn上都找了份代码,不过试验之后发现效果不太理想,所以打算自己写个工具类。参考这两份代码,自己也收获了一点灵感,然后就完成了这份这个工具类,且很好地满足了我的需求。
2. 代码实现
package com.frank.test.arithmetic;import org.apache.commons.lang3.StringUtils;import java.math.BigInteger;
import java.util.HashMap;
import java.util.Map;
//import java.util.Objects;/*** 将英文单词解析成数字。<br>* 核心思想是:将字符串按量级拆分,拆分得到的量级前面的数字值不能超过1000** @author Frank* @date 2016-12-26*/
public class EnglishWordsToNumber {/** 小数点*/private static final String POINT = "point";/** 数量级--百。用得比较多,所以单拿出来*/private static final String HUNDRED_MANITUDE = "hundred";/** 数量级*/private static final String[] MAGNITUDES = {"billion", "million", "thousand", "hundred"};/** 个位数*/private static final String[] DIGITS = {"one", "two", "three", "four", "five", "six", "seven", "eight", "nine"};/** 十位数*/private static final String[] TENS = {"twenty", "thirty", "forty", "fifty", "sixty", "seventy", "eighty", "ninety"};/** 只能单独标识的数字[10, 19]*/private static final String[] TEENS = {"ten", "eleven", "twelve", "thirteen", "fourteen", "fifteen", "sixteen", "seventeen", "eighteen", "nineteen"};private static final Map<String, Integer> map = new HashMap<>();/** 十个阿拉伯数字*/private static final Map<String, String> arabicNumerals = new HashMap<>();static {map.put("zero", 0);// 添加1~9for (int i = 0; i < DIGITS.length; i++) {map.put(DIGITS[i], i + 1);}// 添加10~19for (int i = 0; i < TEENS.length; i++) {map.put(TEENS[i], i + 10);}// 添加TENSfor (int i = 0; i < TENS.length; i++) {map.put(TENS[i], (i + 2) * 10);}for (int i = 0; i < TENS.length; i++) {for (int j = 0; j < DIGITS.length; j++) {map.put(TENS[i] + " " + DIGITS[j], (i + 2) * 10 + (j + 1));map.put(TENS[i] + "-" + DIGITS[j], (i + 2) * 10 + (j + 1));}}// "hundred", "thousand", "million", "billion"map.put("hundred", 100);map.put("thousand", 1000);map.put("million", 1000000);map.put("billion", 1000000000);arabicNumerals.put("zero", "0");for (int i = 0; i < DIGITS.length; i++) {arabicNumerals.put(DIGITS[i], String.valueOf(i + 1));}}/*** 将英文单词解析为一个数字** @param input* @return 如果无法进行合理化解析,就返回原值;否则返回解析后的值*/public static String parse(String input) {if (StringUtils.isBlank(input)) {return input;}// 拆分为整数部分和小数部分String[] parts = input.toLowerCase().replaceAll(" and ", " ").trim().split(POINT);// 如果整数部分只有zeroif (parts[0].trim().equals("zero")) {// 判断小数部分的值if (parts.length == 1) {// 没有小数部分return "0";} else if (parts.length == 2) {String decimal = parseAfterPoint(parts[1]);return decimal == null ? input : "0." + decimal;} else {// point出现了超过一次return input;}}String decimal = null;if (parts.length > 2) {// point出现了超过一次return input;}// 小数部分if (parts.length == 2) {decimal = parseAfterPoint(parts[1]);if (decimal == null) {// 如果有小数部分,但是对小数部分解析后返回null,说明小数部分解析失败return input;}}// 整数部分String part0 = parts[0].trim();BigInteger bi = BigInteger.ZERO;StringBuilder result = new StringBuilder();for (int i = 0; i < MAGNITUDES.length && part0.length() > 0; i++) {Map<String, Object> ret = parseEveryMagnitude(part0, MAGNITUDES[i]);if (ret == null) {return input;}bi = bi.add((BigInteger) ret.get("number"));part0 = ret.get("part0").toString();}// 加上整数部分result.append(bi.toString());// 加上小数部分if (decimal != null) {result.append('.').append(decimal);}return result.toString();}/*** 解析小数部分** @param str* @return 返回null表示小数部分解析失败,返回非null值表示解析成功*/private static String parseAfterPoint(String str) {if (StringUtils.isBlank(str)) {return "0";}StringBuilder builder = new StringBuilder();String[] arr = StringUtils.split(str);for (String s : arr) {String num = arabicNumerals.get(s);if (num == null) {// 也就是说,小数点后面只能出现0~9的英文单词return null;}builder.append(num);}return builder.toString();}/*** 解析得到每个数量级前面的数字,然后和数量级相乘,得到该数量级的值** @param part0* @param magnitude 数量级字符串* @return 如果解析不成功,返回null,否则表示解析成功*/private static Map<String, Object> parseEveryMagnitude(String part0, String magnitude) {Map<String, Object> ret = new HashMap<>(2);if (magnitude.equals(HUNDRED_MANITUDE)) {// 到了hundred这个数量级int num = parseHundred(part0);if (num == -1) {return null;}ret.put("part0", "");ret.put("number", BigInteger.valueOf(num));return ret;}String[] arr = part0.split(magnitude);if (arr.length > 2) {// 字符串中包含多个数量级字符串return null;}if (arr.length == 1) {// arr的长度是1,有两种情况:不包含数量级字符串,或者比如one billionif (part0.contains(magnitude)) {int num = parseHundred(arr[0].trim());if (num == -1) {return null;}int magnitudeNum = map.get(magnitude).intValue();ret.put("part0", "");ret.put("number", BigInteger.valueOf(num).multiply(BigInteger.valueOf(magnitudeNum)));return ret;} else {// 不包含数量级字符串,就将part0原样返回ret.put("part0", part0);ret.put("number", BigInteger.ZERO);return ret;}}if (arr.length == 2) {// 字符串中包含数量级字符串,那么只解析数量级字符串前面的数字内容int num = parseHundred(arr[0].trim());if (num == -1) {return null;}int magnitudeNum = map.get(magnitude).intValue();ret.put("part0", arr[1].trim());ret.put("number", BigInteger.valueOf(num).multiply(BigInteger.valueOf(magnitudeNum)));return ret;}return null;}/*** 解析每个数量级前面的数字及hundred数量级的数字。根据英语数字表示,该部分的值的范围是[1, 999]。<br>** @param hundred* @return 如果解析失败,返回-1,否则返回解析后的值*/private static int parseHundred(String hundred) {String[] arr = hundred.split(HUNDRED_MANITUDE);if (arr.length > 2) {return -1;}if (arr.length == 1) {// arr长度为1,有如下两种情况:one 或者 one hundredInteger num = map.get(arr[0].trim());if (hundred.contains(HUNDRED_MANITUDE)) {// one hundredreturn (num != null && num.intValue() > 0 && num.intValue() < 10) ? num.intValue() * map.get(HUNDRED_MANITUDE) : -1;} else {// onereturn (num != null && num.intValue() > 0 && num.intValue() < 100) ? num.intValue() : -1;}}if (arr.length == 2) {// 包含hundred,那么hundred前面的值的范围就是[1, 9],hundred后面的值的范围就是[1, 99]Integer beforeHundred = map.get(arr[0].trim());Integer afterHundred = map.get(arr[1].trim());return (beforeHundred != null && beforeHundred.intValue() > 0 && beforeHundred.intValue() < 10 &&afterHundred != null && afterHundred.intValue() > 0 && afterHundred.intValue() < 100)? beforeHundred.intValue() * map.get(HUNDRED_MANITUDE).intValue() + afterHundred.intValue(): -1;}return -1;}// public static void main(String[] args) {
// String[] words = {"one", "two", "five", "eleven", "twenty five", "fifty", "one hundred and one",
// "one hundred and fifteen", "one hundred and ninety nine", "one hundred ten",
// "one thousand one",
// "one thousand and one", "one thousand and eleven", "one thousand and ninety nine",
// "one thousand ninety nine", "one thousand one hundred and eleven",
// "one thousand nine hundred and ninety nine", "ten thousand and one",
// "ten thousand and twenty three", "ten thousand two hundred and twenty three",
// "twelve thousand two hundred and twenty three", "one hundred thousand and one",
// "two hundred two million fifty three thousand", "zero"};
// int[] expectedValue = {1, 2, 5, 11, 25, 50, 101,
// 115, 199, 110,
// 1001,
// 1001, 1011, 1099,
// 1099, 1111,
// 1999, 10001,
// 10023, 10223,
// 12223, 100001,
// 202053000, 0};
// for (int i = 0; i < words.length; i++) {
// String word = words[i];
// String num = null;
// try {
// num = parse(word);
// } catch (Exception e) {
// e.printStackTrace();
// }
// boolean result = Objects.equals(String.valueOf(expectedValue[i]), num);
// if (result) {
// System.out.println(word + "--->" + num + ", expected value is " + expectedValue[i] + ", result is " + result);
// } else {
// System.err.println(word + "--->" + num + ", expected value is " + expectedValue[i] + ", result is " + result);
// }
// }
// }
}
3. 简单分析
4. 不足之处
该代码将字符串 " and " 全替换成空字符了,所以对于给定英文字符串,无论有多少个 " and ",都可以进行解析,这从语义上来说不是太正确。
目前的代码只支持解析到billion,如果想要更大的数量级,那么需要做两步:
第一,将更大的数量级单词添加到MAGNITUDES数组的开头,各个数量级按照数量级从大到小排列;
第二,最好是修改静态属性 map 的 Value 的泛型为 BigInteger。
将英文字符串转换为数字相关推荐
- pandas使用read_csv读取文件数据、设置converters参数将百分比字符串转换为数字
pandas使用read_csv读取文件数据.设置converters参数将百分比字符串转换为数字 目录 pandas使用read_csv读取文件数据.设置converters参数将百分比字符串转换为 ...
- Swift3.0语言教程字符串转换为数字值
Swift3.0语言教程字符串转换为数字值 Swift3.0语言教程字符串转换为数字值,在NSString中,开发者可以将字符串转换为数字值,通过这些数字值可以实现一些功能,如加法运算.减法运算等.数 ...
- python 如何将数字字符串转换为数字?
将单个数字字符串转换为数字 def char2num(s):return {'0': 0, '1': 1, '2': 2, '3': 3, '4': 4, '5': 5, '6': 6, '7': 7 ...
- c++ascii码转换为数字_在C++中将字符串转换为数字
有许多情况需要将数字转换为字符串或将字符串转换为数字.本文中提到了一些实现此任务的方法. 将字符串转换为数字 方法1:使用stringstream类或sscanf() 方法2:使用stoi()或ato ...
- Python不使用int()函数把字符串转换为数字
Python不使用int()函数把字符串转换为数字 2018年05月21日 14:18:45 边缘ob边缘ob 阅读数:1035 https://blog.csdn.net/qq_33192555/a ...
- c#语言中怎么样把文本转换成数字,如何将字符串转换为数字 - C# 编程指南 | Microsoft Docs...
如何将字符串转换为数字(C# 编程指南) 02/16/2021 本文内容 你可以调用数值类型(int.long.double 等)中找到的 Parse 或 TryParse 方法或使用 System. ...
- c++字符串转换为数字(stoi, stol, stoul, stoull, stof, stod, stold)
c++字符串转换为数字(stoi, stol, stoul, stoull, stof, stod, stold) 头文件#include <string> 1. stoi 将字符串转换成 ...
- 将字符数组中的字符转换为整型变量的数字 将字符串转换为数字
首先要保证字符数组或字符串中的数据是数字,这样才能转换. 检测字符数组或字符串中是否为数字的方法: 遍历字符数组或字符串,检测是否有非数字字符,若有则无法转换,若没有则可以进行转换. 将字符数组或字符 ...
- 将字符串转换为数字的函数
头文件:#include <stdlib.h> atoi() 函数用来将字符串转换成整数(int),其原型为: int atoi (const char * str); [函数说明]ato ...
- 32.将字符串转换为数字
32.将字符串转换为数字 题目描述 实现函数 atoi .函数的功能为将字符串转化为整数 提示:仔细思考所有可能的输入情况.这个问题没有给出输入的限制,你需要自己考虑所有可能的情况. 输入 " ...
最新文章
- nginx-启动gzip、虚拟主机、请求转发、负载均衡
- 编程语言之类型之间转换
- [zjoi2015]幻想乡战略游戏
- vuex 管理vue-router的传值
- Java历经20年沧桑,将持续革新
- A sample that using the completion port I/O model
- 一文读懂质量保证和质量控制
- Java后端开发工程师学习笔记【狂神说Java笔记】
- python爬虫:用scrapy框架爬取链家网房价信息并存入mongodb
- 数学竞赛辅导陈启浩pdf_高中数学竞赛考试大纲及必备辅导书汇总,尖子生请收好...
- 联想微型计算机Q150,联想Q150E电脑安装攻略
- linux添加windows隐藏属性,解决文件夹隐藏属性无法取消的办法
- i9 9900es版,QQC0满载功耗测试
- Win10搭建Web局域网文件共享库
- 【学习笔记】PHP进阶
- 什么是Socket?websocket和socket区别?
- Kubernetes(k8s)基础之二:容器编排介绍及概念
- 模拟电路软件oracle,电子商务模拟教学平台
- 最新西游H5复仇者手游端系统源码+附带文本教程
- ios开发中常用的几种辅助方法