如何将整数转换为其口头表示?

58
有没有一个库或者类/函数可以用来将整数转换成它的口头表示?
示例输入:
4,567,788
示例输出:
四百五十六万七千七百八十八

希望我的简短方法有所帮助。 - Mohsen Alyafei
16个回答

68

目前最好、最强大的库,肯定是 Humanizer。它是开源的,并且作为 NuGet 包可用:

Console.WriteLine(4567788.ToWords()); // => four million five hundred and sixty-seven thousand seven hundred and eighty-eight

它还拥有广泛的工具,解决每个应用程序都有的小问题,例如字符串、枚举、日期时间、时间间隔等,并支持许多不同的语言。
Console.WriteLine(4567788.ToOrdinalWords().Underscore().Hyphenate().ApplyCase(LetterCasing.AllCaps)); // => FOUR-MILLION-FIVE-HUNDRED-AND-SIXTY-SEVEN-THOUSAND-SEVEN-HUNDRED-AND-EIGHTY-EIGHTH

2
哇,这是多语言的:南非荷兰语、阿拉伯语、孟加拉语、巴西葡萄牙语、荷兰语、英语、波斯语、芬兰语、法语、德语、希伯来语、意大利语、挪威博克马尔语、波兰语、罗马尼亚语、俄语、塞尔维亚西里尔文、塞尔维亚语、斯洛文尼亚语、西班牙语、乌克兰语。请访问 https://github.com/Humanizr/Humanizer/tree/dev/src/Humanizer/Localisation/NumberToWords - Stéphane Gourichon
4
被低估的答案。这是正确回答该问题的答案。 - Ian Grainger
1
有没有办法做相反的事情? - ryanwebjackson
这就是大家一直在寻找的东西。谢谢。 - Dennis Henry
这真的很令人印象深刻!! - Rafi Henig
1
在我看来,这仍然是至今最相关和最好的答案! - TDiblik

28

如果您使用在将数字转换为单词C#中找到的代码,并且您需要用于十进制数,以下是如何操作:

public string DecimalToWords(decimal number)
{
    if (number == 0)
        return "zero";

    if (number < 0)
        return "minus " + DecimalToWords(Math.Abs(number));

    string words = "";

    int intPortion = (int)number;
    decimal fraction = (number - intPortion)*100;
    int decPortion = (int)fraction;

    words = NumericToWords(intPortion);
    if (decPortion > 0)
    {
        words += " and ";
        words += NumericToWords(decPortion);
    }
    return words;
}

当我在金额中使用小数时,我不希望“and”出现两次,一次是在“Number to Words”,一次是在“Decimal”中,如果有小数,它应该只与小数一起出现,而不是没有小数时。 - Patrick

11

一个完全递归的版本:

private static string[] ones = {
    "zero", "one", "two", "three", "four", "five", "six", "seven", "eight", "nine",
    "ten", "eleven", "twelve", "thirteen", "fourteen", "fifteen", "sixteen", "seventeen", "eighteen", "nineteen",
};

private static string[] tens = { "zero", "ten", "twenty", "thirty", "forty", "fifty", "sixty", "seventy", "eighty", "ninety" };

private static string[] thous = { "hundred", "thousand", "million", "billion", "trillion", "quadrillion" };

private static string fmt_negative = "negative {0}";
private static string fmt_dollars_and_cents = "{0} dollars and {1} cents";
private static string fmt_tens_ones = "{0}-{1}"; // e.g. for twenty-one, thirty-two etc. You might want to use an en-dash or em-dash instead of a hyphen.
private static string fmt_large_small = "{0} {1}"; // stitches together the large and small part of a number, like "{three thousand} {five hundred forty two}"
private static string fmt_amount_scale = "{0} {1}"; // adds the scale to the number, e.g. "{three} {million}";

public static string ToWords(decimal number) {
    if (number < 0)
        return string.format(fmt_negative, ToWords(Math.Abs(number)));

    int intPortion = (int)number;
    int decPortion = (int)((number - intPortion) * (decimal) 100);

    return string.Format(fmt_dollars_and_cents, ToWords(intPortion), ToWords(decPortion));
}

private static string ToWords(int number, string appendScale = "") {
    string numString = "";
    // if the number is less than one hundred, then we're mostly just pulling out constants from the ones and tens dictionaries
    if (number < 100) {
        if (number < 20)
            numString = ones[number];
        else {
            numString = tens[number / 10];
            if ((number % 10) > 0)
                numString = string.Format(fmt_tens_ones, numString, ones[number % 10]);
        }
    } else {
        int pow = 0; // we'll divide the number by pow to figure out the next chunk
        string powStr = ""; // powStr will be the scale that we append to the string e.g. "hundred", "thousand", etc.

        if (number < 1000) { // number is between 100 and 1000
            pow = 100; // so we'll be dividing by one hundred
            powStr = thous[0]; // and appending the string "hundred"
        } else { // find the scale of the number
            // log will be 1, 2, 3 for 1_000, 1_000_000, 1_000_000_000, etc.
            int log = (int)Math.Log(number, 1000);
            // pow will be 1_000, 1_000_000, 1_000_000_000 etc.
            pow = (int)Math.Pow(1000, log);
            // powStr will be thousand, million, billion etc.
            powStr = thous[log];
        }

        // we take the quotient and the remainder after dividing by pow, and call ToWords on each to handle cases like "{five thousand} {thirty two}" (curly brackets added for emphasis)
        numString = string.Format(fmt_large_small, ToWords(number / pow, powStr), ToWords(number % pow)).Trim();
    }

    // and after all of this, if we were passed in a scale from above, we append it to the current number "{five} {thousand}"
    return string.Format(fmt_amount_scale, numString, appendScale).Trim();
}

目前它可以处理(短刻度)万亿级别的数字。只需更改thous变量,即可添加对更大数字或长刻度的支持。


这正是我所需要的。谢谢。 - Marco Alves
有一种更短的方法也可以很有效,可以看看我对这个问题的解决方案。希望它有用。 - Mohsen Alyafei

3
这个解决方案利用了C# 7.0对本地函数的支持。我还使用了新的数字分隔符,使更大的数字更易读。
public static class NumberExtensions
{
    private const string negativeWord = "negative";
    private static readonly Dictionary<ulong, string> _wordMap = new Dictionary<ulong, string>
    {
        [1_000_000_000_000_000_000] = "quintillion",
        [1_000_000_000_000_000] = "quadrillion",
        [1_000_000_000_000] = "trillion",
        [1_000_000_000] = "billion",
        [1_000_000] = "million",
        [1_000] = "thousand",
        [100] = "hundred",
        [90] = "ninety",
        [80] = "eighty",
        [70] = "seventy",
        [60] = "sixty",
        [50] = "fifty",
        [40] = "forty",
        [30] = "thirty",
        [20] = "twenty",
        [19] = "nineteen",
        [18] = "eighteen",
        [17] = "seventeen",
        [16] = "sixteen",
        [15] = "fifteen",
        [14] = "fourteen",
        [13] = "thirteen",
        [12] = "twelve",
        [11] = "eleven",
        [10] = "ten",
        [9] = "nine",
        [8] = "eight",
        [7] = "seven",
        [6] = "six",
        [5] = "five",
        [4] = "four",
        [3] = "three",
        [2] = "two",
        [1] = "one",
        [0] = "zero"
    };

    public static string ToWords(this short num)
    {
        var words = ToWords((ulong)Math.Abs(num));
        return num < 0 ? $"{negativeWord} {words}" : words;
    }

    public static string ToWords(this ushort num)
    {
        return ToWords((ulong)num);
    }

    public static string ToWords(this int num)
    {
        var words = ToWords((ulong)Math.Abs(num));
        return num < 0 ? $"{negativeWord} {words}" : words;
    }

    public static string ToWords(this uint num)
    {
        return ToWords((ulong)num);
    }

    public static string ToWords(this long num)
    {
        var words = ToWords((ulong)Math.Abs(num));
        return num < 0 ? $"{negativeWord} {words}" : words;
    }

    public static string ToWords(this ulong num)
    {
        var sb = new StringBuilder();
        var delimiter = String.Empty;

        void AppendWords(ulong dividend)
        {
            void AppendDelimitedWord(ulong key)
            {
                sb.Append(delimiter);
                sb.Append(_wordMap[key]);
                delimiter = 20 <= key && key < 100 ? "-" : " ";
            }
            
            if (_wordMap.ContainsKey(dividend))
            {
                AppendDelimitedWord(dividend);
            }
            else
            {
                var divisor = _wordMap.First(m => m.Key <= dividend).Key;
                var quotient = dividend / divisor;
                var remainder = dividend % divisor;

                if (quotient > 0 && divisor >= 100)
                {
                    AppendWords(quotient);
                }

                AppendDelimitedWord(divisor);

                if (remainder > 0)
                {   
                    AppendWords(remainder);
                }
            }
        }

        AppendWords(num);
        return sb.ToString();
    }    
}

关键在于最后一个 ToWords 重载函数。


嗨,克里斯。我认为我今天发布的解决方案(虽然晚了)提供了一个简单而简短的解决方案。请参见上文。希望你能进一步改进它。 - Mohsen Alyafei
它是如何利用 C# 7.0 对本地函数的支持的呢? - Peter Mortensen

2

西班牙语版本:

public static string numeroALetras(int number)
{
    if (number == 0)
        return "cero";

    if (number < 0)
        return "menos " + numeroALetras(Math.Abs(number));

    string words = "";

    if ((number / 1000000) > 0)
    {
        words += numeroALetras(number / 1000000) + " millón ";
        number %= 1000000;
    }

    if ((number / 1000) > 0)
    {
        words += (number / 1000) == 1? "mil ": numeroALetras(number / 1000) + " mil ";
        number %= 1000;
    }
    if ((number / 100) == 1)
    {
        if (number == 100)
            words += "cien";
        else words += (number / 100)> 1? numeroALetras(number / 100) + " ciento ":"ciento ";
        number %= 100;
    }
    if ((number / 100) > 1)
    {
        var hundredMap = new[] {"","", "dosc", "tresc", "cuatroc", "quin", "seisc", "sietec", "ochoc", "novec" };
        if (number > 199)
            words += hundredMap[number/100] + "ientos ";
        else {
            words += numeroALetras(number / 100) + " ientos ";
        }
        number %= 100;
    }

    if (number > 0)
    {
        if (words != "")
            words += " ";

        var unitsMap = new[] { "cero", "uno", "dos", "tres", "cuatro", "cinco", "seis", "siete", "ocho", "nueve", "diez", "once", "doce", "trece", "catorce", "quince", "dieciseis", "diecisiete", "dieciocho", "diecinueve", "veinte" };
        var tensMap = new[] { "cero", "diez", "veinti", "treinta", "cuarenta", "cincuenta", "sesenta", "setenta", "ochenta", "noventa" };
        
        if (number < 21)
            words += unitsMap[number];
        else
        {                    
            words += tensMap[number / 10];
            if ((number % 10) > 0)
                words += ((number % 10)>2?" y ": "") + unitsMap[number % 10];                    
        }
    }

    return words;
}

2

JavaScript版本:

Number.prototype.numberToWords = function () {
    var unitsMap = ["zero", "one", "two", "three", "four", "five", "six", "seven", "eight", "nine", "ten", "eleven", "twelve", "thirteen", "fourteen", "fifteen", "sixteen", "seventeen", "eighteen", "nineteen"];
    var tensMap = ["zero", "ten", "twenty", "thirty", "forty", "fifty", "sixty", "seventy", "eighty", "ninety"];

    var num = this.valueOf();
    if (Math.round(num == 0)) {
        return "zero";
    }
    if (num < 0) {
        var positivenum = Math.abs(num);
        return "minus " + Number(positivenum).numberToWords();
    }
    var words = "";
    if (Math.floor(num / 1000000) > 0) {
        words += Math.floor(num / 1000000).numberToWords() + " million ";
        num = Math.floor(num % 1000000);
    }
    if (Math.floor(num / 1000) > 0) {
        words += Math.floor(num / 1000).numberToWords() + " thousand ";
        num = Math.floor(num % 1000);
    }
    if (Math.floor(num / 100) > 0) {
        words += Math.floor(num / 100).numberToWords() + " hundred ";
        num = Math.floor(num % 100);
    }
    if (Math.floor(num > 0)) {
        if (words != "") {
            words += "and ";
        }
        if (num < 20) {
        words += unitsMap[num];
        }
        else {
            words += tensMap[Math.floor(num / 10)];
            if ((num % 10) > 0) {
                words += "-" + unitsMap[Math.round(num % 10)];
            }
        }
    }
    return words.trim();
}

2

我使用C#创建了一个Web API,可以将数字转换为文字。

输入可以是48小时时间格式的整数或带小数点的数字。

调用来自前端应用程序,使用Ajax Post方法,并在网页中返回转换后的结果。

我在GitHub上分享了这个项目:https://github.com/marvinglennlacuna/NumbersToWordsConverter.Api

以下是技术实现:

  1. MVC结构
  2. API控制器
  3. 服务
  4. 模型
  5. 错误处理
  6. 使用MSTest进行单元测试
  7. 代码覆盖率 - 98%
  8. jQuery

并且有关以下内容的技术文档:

  1. 目的
  2. 先决条件
  3. 功能要求
  4. 流程图和输出

通过网页返回结果(US-001)

US-001 通过网页将数字转换为文字的过程

Enter image description here

US-001 通过网页输出将数字转换为文字

Enter image description here

通过 Postman 返回结果(US-002)

US-002 - 通过 Postman 进程将数字转换为单词

Enter image description here

US-002 - 通过 Postman 输出将数字转换为单词

Enter image description here


1

我编写了一个C#函数NumIntToWords(String NumIn),它提供了一种简单、短小、高效的方法来将整数转换为英文单词,符合美式英语语法规则。

该函数使用了我设计的SLST(Single Loop String Triplet)方法。

与我在这里和其他地方看到的其他方法不同,该函数具有以下特点:

  • 简单、短小、快速。
  • 它不使用switch/case语句。
  • 它不需要在字符串中使用额外的空格或修剪字符串。
  • 它不使用递归。
  • 它不使用数学函数(仅使用一个数学函数来创建字符串三元组)。
  • 您可以通过简单增加比万更大的规模数组来增加规模,而无需进行任何额外的编码。
  • 它编译成非常短的代码。
  • 没有额外的库。

我选择将要转换的整数作为字符串传递,以便您可以轻松处理非常大的整数。

您可以像这样使用它:

Console.WriteLine(NumIntToWords("1991345678974"));

输出:

一万九千九百九十一亿三千四百五十六万七千八百九十四

C# SLST函数方法(c)Mohsen Alyafei:

/*********************************************************************
* @function    : NumIntToWords(String NumIn)
* @purpose     : Converts Unsigned Integers to Words
*                Using the SLST Method (C) Mohsen Alyafei 2019.
*                Does not check for Non-Numerics or +/- signs
* @version     : 2.11
* @author      : Mohsen Alyafei
* @Licence     : MIT
* @date        : 03 April 2022
* @in_param    : {NumIn} (Required): Number in string format
* @returns     : {string}: The number in English Words US Grammar
**********************************************************************/
public static string NumIntToWords(String NumIn) {
    if (NumIn.TrimStart('0') == "")
        return "Zero";                   // If empty or zero return Zero

    string[] T10s = {"", "One", "Two", "Three", "Four", "Five", "Six",
                     "Seven", "Eight", "Nine", "Ten", "Eleven", "Twelve",
                     "Thirteen", "Fourteen", "Fifteen", "Sixteen",
                     "Seventeen", "Eighteen", "Nineteen"},
             T20s = {"", "", "Twenty", "Thirty", "Forty", "Fifty",
                     "Sixty", "Seventy","Eighty","Ninety"},
           Scales = {"", "Thousand", "Million", "Billion", "Trillion"}; // Increase scale here (unlimited)

    NumIn = new String('0', NumIn.Length * 2 % 3) + NumIn; // Make it a String Triplet
    String numToWords = "", wordTriplet, Triplet;

    for (int digits = NumIn.Length; digits > 0; digits -= 3) { // Single Loop
        Triplet = NumIn.Substring(NumIn.Length - digits, 3);   // Get next Triplet
        if (Triplet != "000") {                                // Convert Only if not empty
            wordTriplet = "";
            int ScalePos = digits / 3 - 1,                     // Scale name position
                    hund = int.Parse("" + Triplet[0]),
                    tens = int.Parse(Triplet.Substring(1, 2)),
                    ones = int.Parse("" + Triplet[2]);
             wordTriplet = (hund>0 ? T10s[hund] + " Hundred" : "") +
                           (tens>0 && hund>0 ? " " : "") +
                           (tens<20 ? T10s[tens]: T20s[int.Parse("" + Triplet[1])] + (ones>0? "-" + T10s[ones] : "")) +
                           (ScalePos>0 ? " " : "") + Scales[ScalePos];  // Add Scale Name to Triplet Word
             numToWords += (numToWords != "" ? " " : "") + wordTriplet; // Concatenate Next Triplet Word
        }
    }                   // Loop for the next Triplet
    return numToWords;  // Return full Number in Words
}

1

http://www.exchangecore.com/blog/convert-number-words-c-sharp-console-application/提供了一些C#脚本,看起来可以处理非常大的数字和非常小的小数。

using System;
using System.Collections.Generic;
using System.Text;

namespace NumWords
{
    class Program
    {
        // PROGRAM HANDLES NEGATIVE AND POSITIVE DOUBLES


        static String NumWordsWrapper(double n)
        {
            string words = "";
            double intPart;
            double decPart = 0;
            if (n == 0)
                return "zero";
            try {
                string[] splitter = n.ToString().Split('.');
                intPart = double.Parse(splitter[0]);
                decPart = double.Parse(splitter[1]);
            } catch {
                intPart = n;
            }

            words = NumWords(intPart);

            if (decPart > 0) {
                if (words != "")
                    words += " and ";
                int counter = decPart.ToString().Length;
                switch (counter) {
                    case 1: words += NumWords(decPart) + " tenths"; break;
                    case 2: words += NumWords(decPart) + " hundredths"; break;
                    case 3: words += NumWords(decPart) + " thousandths"; break;
                    case 4: words += NumWords(decPart) + " ten-thousandths"; break;
                    case 5: words += NumWords(decPart) + " hundred-thousandths"; break;
                    case 6: words += NumWords(decPart) + " millionths"; break;
                    case 7: words += NumWords(decPart) + " ten-millionths"; break;
                }
            }
            return words;
        }

        static String NumWords(double n) //converts double to words
        {
            string[] numbersArr = new string[] { "one", "two", "three", "four", "five", "six", "seven", "eight", "nine", "ten", "eleven", "twelve", "thirteen", "fourteen", "fifteen", "sixteen", "seventeen", "eighteen", "nineteen" };
            string[] tensArr = new string[] { "twenty", "thirty", "fourty", "fifty", "sixty", "seventy", "eighty", "ninty" };
            string[] suffixesArr = new string[] { "thousand", "million", "billion", "trillion", "quadrillion", "quintillion", "sextillion", "septillion", "octillion", "nonillion", "decillion", "undecillion", "duodecillion", "tredecillion", "Quattuordecillion", "Quindecillion", "Sexdecillion", "Septdecillion", "Octodecillion", "Novemdecillion", "Vigintillion" };
            string words = "";

            bool tens = false;

            if (n < 0) {
                words += "negative ";
                n *= -1;
            }

            int power = (suffixesArr.Length + 1) * 3;

            while (power > 3) {
                double pow = Math.Pow(10, power);
                if (n >= pow) {
                    if (n % pow > 0) {
                        words += NumWords(Math.Floor(n / pow)) + " " + suffixesArr[(power / 3) - 1] + ", ";
                    } else if (n % pow == 0) {
                        words += NumWords(Math.Floor(n / pow)) + " " + suffixesArr[(power / 3) - 1];
                    }
                    n %= pow;
                }
                power -= 3;
            }
            if (n >= 1000) {
                if (n % 1000 > 0) words += NumWords(Math.Floor(n / 1000)) + " thousand, ";
                else words += NumWords(Math.Floor(n / 1000)) + " thousand";
                n %= 1000;
            }
            if (0 <= n && n <= 999) {
                if ((int)n / 100 > 0) {
                    words += NumWords(Math.Floor(n / 100)) + " hundred";
                    n %= 100;
                }
                if ((int)n / 10 > 1) {
                    if (words != "")
                        words += " ";
                    words += tensArr[(int)n / 10 - 2];
                    tens = true;
                    n %= 10;
                }

                if (n < 20 && n > 0) {
                    if (words != "" && tens == false)
                        words += " ";
                    words += (tens ? "-" + numbersArr[(int)n - 1] : numbersArr[(int)n - 1]);
                    n -= Math.Floor(n);
                }
            }

            return words;

        }
        static void Main(string[] args)
        {
            Console.Write("Enter a number to convert to words: ");
            Double n = Double.Parse(Console.ReadLine());

            Console.WriteLine("{0}", NumWordsWrapper(n));
        }
    }
}

编辑:从博客文章中带来了代码


0

更详细的功能实现:

public static class NumberToWord
{
    private static readonly Dictionary<long, string> MyDictionary = new Dictionary<long, string>();

    static NumberToWord()
    {
        MyDictionary.Add(1000000000000000, "quadrillion");
        MyDictionary.Add(1000000000000, "trillion");
        MyDictionary.Add(1000000000, "billion");
        MyDictionary.Add(1000000, "million");
        MyDictionary.Add(1000, "thousand");
        MyDictionary.Add(100, "hundread");
        MyDictionary.Add(90, "ninety");
        MyDictionary.Add(80, "eighty");
        MyDictionary.Add(70, "seventy");
        MyDictionary.Add(60, "sixty");
        MyDictionary.Add(50, "fifty");
        MyDictionary.Add(40, "fourty");
        MyDictionary.Add(30, "thirty");
        MyDictionary.Add(20, "twenty");
        MyDictionary.Add(19, "nineteen");
        MyDictionary.Add(18, "eighteen");
        MyDictionary.Add(17, "seventeen");
        MyDictionary.Add(16, "sixteen");
        MyDictionary.Add(15, "fifteen");
        MyDictionary.Add(14, "fourteen");
        MyDictionary.Add(13, "thirteen");
        MyDictionary.Add(12, "twelve");
        MyDictionary.Add(11, "eleven");
        MyDictionary.Add(10, "ten");
        MyDictionary.Add(9, "nine");
        MyDictionary.Add(8, "eight");
        MyDictionary.Add(7, "seven");
        MyDictionary.Add(6, "six");
        MyDictionary.Add(5, "five");
        MyDictionary.Add(4, "four");
        MyDictionary.Add(3, "three");
        MyDictionary.Add(2, "two");
        MyDictionary.Add(1, "one");
        MyDictionary.Add(0, "zero");
    }

    /// <summary>
    /// To the verbal.
    /// </summary>
    /// <param name="value">The value.</param>
    /// <returns></returns>
    public static string ToVerbal(this int value)
    {
        return ToVerbal((long) value);
    }

    /// <summary>
    /// To the verbal.
    /// </summary>
    /// <param name="value">The value.</param>
    /// <returns></returns>
    public static string ToVerbal(this long value)
    {
        if (value == 0)
            return MyDictionary[value];

        if (value < 0)
            return $" negative {ToVerbal(Math.Abs(value))}";

        var builder = new StringBuilder();

        for (var i = 1000000000000000; i >= 1000; i = i/1000)
            value = ConstructWord(value, builder, i);

        value = ConstructWord(value, builder, 100);

        for (var i = 90; i >= 20; i = i - 10)
            value = ConstructWordForTwoDigit(value, builder, i);

        if (MyDictionary.ContainsKey(value))
            builder.AppendFormat("{0}" + MyDictionary[value], builder.Length > 0
                ? " "
                : string.Empty);

        return builder.ToString();
    }

    private static long ConstructWord(long value, StringBuilder builder, long key)
    {
        if (value >= key)
        {
            var unit = (int) (value/key);
            value -= unit*key;
            builder.AppendFormat(" {0} {1} " + MyDictionary[key], builder.Length > 0
                ? ", "
                : string.Empty, ToVerbal(unit));
        }
        return value;
    }

    private static long ConstructWordForTwoDigit(long value, StringBuilder builder, long key)
    {
        if (value >= key)
        {
            value -= key;
            builder.AppendFormat(" {0} " + MyDictionary[key], builder.Length > 0
                ? " "
                : string.Empty);
        }
        return value;
    }
}

我使用的字符串插值仅在4.6.1版本中可用。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接