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. 简单分析

先来看一个数字:5,783,456 --> five million seven hundred and eighty-three thousand four hundred and fifty-six
将英文单词转换成数字,首先得分析一下英文的数字的读法。
第一,英文的数字,一般来说,是每隔三位有一个单词来表示该量级;
第二,两个量级之间,数字的数值范围是[1, 999];
第三,按数量级大小,将英文单词从左往右进行解析。

4. 不足之处

该代码将字符串 " and " 全替换成空字符了,所以对于给定英文字符串,无论有多少个 " and ",都可以进行解析,这从语义上来说不是太正确。

目前的代码只支持解析到billion,如果想要更大的数量级,那么需要做两步:

第一,将更大的数量级单词添加到MAGNITUDES数组的开头,各个数量级按照数量级从大到小排列;

第二,最好是修改静态属性 map 的 Value 的泛型为 BigInteger。

将英文字符串转换为数字相关推荐

  1. pandas使用read_csv读取文件数据、设置converters参数将百分比字符串转换为数字

    pandas使用read_csv读取文件数据.设置converters参数将百分比字符串转换为数字 目录 pandas使用read_csv读取文件数据.设置converters参数将百分比字符串转换为 ...

  2. Swift3.0语言教程字符串转换为数字值

    Swift3.0语言教程字符串转换为数字值 Swift3.0语言教程字符串转换为数字值,在NSString中,开发者可以将字符串转换为数字值,通过这些数字值可以实现一些功能,如加法运算.减法运算等.数 ...

  3. python 如何将数字字符串转换为数字?

    将单个数字字符串转换为数字 def char2num(s):return {'0': 0, '1': 1, '2': 2, '3': 3, '4': 4, '5': 5, '6': 6, '7': 7 ...

  4. c++ascii码转换为数字_在C++中将字符串转换为数字

    有许多情况需要将数字转换为字符串或将字符串转换为数字.本文中提到了一些实现此任务的方法. 将字符串转换为数字 方法1:使用stringstream类或sscanf() 方法2:使用stoi()或ato ...

  5. Python不使用int()函数把字符串转换为数字

    Python不使用int()函数把字符串转换为数字 2018年05月21日 14:18:45 边缘ob边缘ob 阅读数:1035 https://blog.csdn.net/qq_33192555/a ...

  6. c#语言中怎么样把文本转换成数字,如何将字符串转换为数字 - C# 编程指南 | Microsoft Docs...

    如何将字符串转换为数字(C# 编程指南) 02/16/2021 本文内容 你可以调用数值类型(int.long.double 等)中找到的 Parse 或 TryParse 方法或使用 System. ...

  7. c++字符串转换为数字(stoi, stol, stoul, stoull, stof, stod, stold)

    c++字符串转换为数字(stoi, stol, stoul, stoull, stof, stod, stold) 头文件#include <string> 1. stoi 将字符串转换成 ...

  8. 将字符数组中的字符转换为整型变量的数字 将字符串转换为数字

    首先要保证字符数组或字符串中的数据是数字,这样才能转换. 检测字符数组或字符串中是否为数字的方法: 遍历字符数组或字符串,检测是否有非数字字符,若有则无法转换,若没有则可以进行转换. 将字符数组或字符 ...

  9. 将字符串转换为数字的函数

    头文件:#include <stdlib.h> atoi() 函数用来将字符串转换成整数(int),其原型为: int atoi (const char * str); [函数说明]ato ...

  10. 32.将字符串转换为数字

    32.将字符串转换为数字 题目描述 实现函数 atoi .函数的功能为将字符串转化为整数 提示:仔细思考所有可能的输入情况.这个问题没有给出输入的限制,你需要自己考虑所有可能的情况. 输入 " ...

最新文章

  1. nginx-启动gzip、虚拟主机、请求转发、负载均衡
  2. 编程语言之类型之间转换
  3. [zjoi2015]幻想乡战略游戏
  4. vuex 管理vue-router的传值
  5. Java历经20年沧桑,将持续革新
  6. A sample that using the completion port I/O model
  7. 一文读懂质量保证和质量控制
  8. Java后端开发工程师学习笔记【狂神说Java笔记】
  9. python爬虫:用scrapy框架爬取链家网房价信息并存入mongodb
  10. 数学竞赛辅导陈启浩pdf_高中数学竞赛考试大纲及必备辅导书汇总,尖子生请收好...
  11. 联想微型计算机Q150,联想Q150E电脑安装攻略
  12. linux添加windows隐藏属性,解决文件夹隐藏属性无法取消的办法
  13. i9 9900es版,QQC0满载功耗测试
  14. Win10搭建Web局域网文件共享库
  15. 【学习笔记】PHP进阶
  16. 什么是Socket?websocket和socket区别?
  17. Kubernetes(k8s)基础之二:容器编排介绍及概念
  18. 模拟电路软件oracle,电子商务模拟教学平台
  19. 最新西游H5复仇者手游端系统源码+附带文本教程
  20. ios开发中常用的几种辅助方法

热门文章

  1. WLAN和WiFi是同一个东西吗
  2. 修改植物大战僵尸游戏存档
  3. 中国电信修改光猫路由模式为桥接模式
  4. JavaWeb如何判断账户密码
  5. 基于Transformer的时空融合网络地铁客流预测模型
  6. 配置文件(properties类)
  7. IDEA 配置文件位置
  8. [爬虫系列(三)]用多线程爬取百度贴吧默认表情
  9. PyTorch 音频处理教程
  10. 用友汽车IPO过会:拟募资5.6亿 高瓴刚参与母公司定增