I have a C# method that converts integer numbers into their English word counterparts; however, how can I implement this so it'll work with a string.
For example, if I have the following string:
There was a dog. The dog ate 1000 bones. After eating, he was very sleepy. He slept for 12 hours.
I want it to parse it and return:
There was a dog. The dog ate one thousand bones. After eating, he was very sleepy. He slept for twelve hours.
How would I go by taking the numbers out of the sentence, and using the conversion method (below)?
public static string NumberToWords(int number)
{
if (number == 0)
return "zero";
if (number < 0)
return "minus " + NumberToWords(Math.Abs(number));
string words = "";
if ((number / 1000000) > 0)
{
words += NumberToWords(number / 1000000) + " million ";
number %= 1000000;
}
if ((number / 1000) > 0)
{
words += NumberToWords(number / 1000) + " thousand ";
number %= 1000;
}
if ((number / 100) > 0)
{
words += NumberToWords(number / 100) + " hundred ";
number %= 100;
}
if (number > 0)
{
if (words != "")
words += "and ";
var unitsMap = new[] { "zero", "one", "two", "three", "four", "five", "six", "seven", "eight", "nine", "ten", "eleven", "twelve", "thirteen", "fourteen", "fifteen", "sixteen", "seventeen", "eighteen", "nineteen" };
var tensMap = new[] { "zero", "ten", "twenty", "thirty", "forty", "fifty", "sixty", "seventy", "eighty", "ninety" };
if (number < 20)
words += unitsMap[number];
else
{
words += tensMap[number / 10];
if ((number % 10) > 0)
words += "-" + unitsMap[number % 10];
}
}
return words;
}
I tried doing a foreach, like this, but it didn't work...
var matches = Regex.Matches(myString, "[0-9]+");
foreach(var match in matches)
{
NumberToWords(match);
}
You could use Split and LINQ:
var input = "There was a dog. The dog ate 1000 bones. After eating, he was very sleepy. He slept for 12 hours.";
var invalidChars = new [] { ',', ';', '-', '.' };
var words = input.Split(' ')
.Select(x =>
{
if (x.All(char.IsDigit) || x.Trim(invalidChars).All(char.IsDigit))
return NumberToWords(int.Parse(x.Trim(invalidChars)));
return x;
});
var output = string.Join(" ", words);
Btw I assume that NumberToWord method is working correctly.
You could use Selman22's approach, but in case you are not using a version of .NET with LINQ, you could use the RegularExpression replace function instead of Match.
http://msdn.microsoft.com/en-us/library/cft8645c(v=vs.110).aspx
You would just need a MatchEvaluator delegate like this:
static string matchEvaluator(Match m)
{
return NumberToWords(int.Parse(m.ToString()));
}
Much more simple way is to use Regex.Replace method utilizing MatchEvaluator.
string input = "There was a dog. The dog ate 1000 bones. After eating, he was very sleepy. He slept for 12 hours.";
string output = Regex.Replace(input, #"\d+", m => NumberToWords(int.Parse(m.Value)));
Minimal Changes to your existing code:
string s = "There was a dog. The dog ate 1000 bones. After eating, he was very sleepy. He slept for 12 hours.";
foreach (var match in Regex.Matches(s, "[0-9]+"))
{
string number = match.ToString();
s = s.Replace(number, NumberToWords(int.Parse(number)));
}
Console.WriteLine(s);
Related
I am following a tutorial regarding converting an integer number into a spoken-word equivalent in C#.
I am getting a bit confused about the three digit rule.
// Single-digit and small number names
private static string[] smallNumbers = new string[]
{
"zero", "one", "two", "three", "four", "five", "six", "seven", "eight",
"nine", "ten", "eleven", "twelve", "thirteen", "fourteen", "fifteen",
"sixteen", "seventeen", "eighteen", "nineteen"
};
// Tens number names from twenty upwards
private static string[] tens = new string[]
{
"", "", "twenty", "thirty", "forty", "fifty", "sixty", "seventy",
"eighty", "ninety"
};
// Scale number names for use during recombination
private static string[] scaleNumbers = new string[]
{
"", "thousand", "million", "billion", "trillion",
"quadrillion", "quintillion"
};
public static string ToWords(this BigInteger number)
{
if (number == 0)
{
return "zero";
}
int[] digitGroups = new int[groups];
// Ensure a positive number to extract from
var positive = BigInteger.Abs(number);
// Extract the three-digit groups
for (int i = 0; i < groups; i++)
{
digitGroups[i] = (int)(positive % 1000);
positive /= 1000;
}
//The rest of the code..
}
I am assuming now we are converting a number and its value is 25111.
In the for-loop function, the return value of (int)(positive % 1000) should be 111. The 111 does not match any elements in digitalGroups array.
I don't quite get it, can someone explain it to me? Thanks in advance.
The code you are showing us is not matching but rather assigning the value 111 to the first item of the digitGroupsArray.
How many items has digitGroupsArray? I don't know, it depends on the 'groups' variable value, which we can't see in the code excerpt.
Here:
int[] digitGroups = new int[groups];
you're creating a new empty integer array called digitGroups with the length of (int) 'group' value.
While this,
for (int i = 0; i < groups; i++)
{
digitGroups[i] = (int)(positive % 1000);
positive /= 1000;
}
is a cylce, an iteration. And note that each time the 'positive' variable gets divided by 1000 (like positive = positive / 1000).
The result will be like this:
digitGroups[0] = (int)(25111 % 1000) // first item of digitGroupsarray
'positive' gets divided (25111 / 1000) next time will be 25
digitGroups[1] = (int)(25 % 1000) // second item of digitGroupsarray
'positive' gets divided again (25 / 1000) next time will be 0
and so on...
In these situation is very common and useful to log the values and debug the cycle. It really clears up your mind.
Working example:
static void Main(string[] args)
{
int groups = 3;
int[] digitGroups = new int[groups];
int positive = 25111;
for (int x = 0; x < groups; x++)
{
Console.WriteLine("positive value is: " + positive);
digitGroups[x] = (int)(positive % 1000);
positive /= 1000;
Console.WriteLine("item number (index): " + x + " value: " + digitGroups[x]);
}
}
Outputs:
positive value is: 25111
item number (index): 0 value: 111
positive value is: 25
item number (index): 1 value: 25
positive value is: 0
item number (index): 2 value: 0
Okay i might not have explained it to the best of my ability but i'm a beginner and i would like to make a piece of code that does this :
you have a string and you need to find each vowel in it and multiply each vowel's position in the string by its position in the alphabet and add all the sums together
example : steve: has 2 vowels the first e's position is 3 and its position in the alphabet is 5. and the second's position in the alphabet and the string is 5
so the sum is 5*3 + 5*5 = 40
this is what i did . idk what to do now or how to approach it
var vowels = new char[] {'a', 'e', 'i', 'o', 'u', 'y', 'A','E','I', 'O', 'U','Y'};
var chars = new List<char>();
List<int> indexes = new List<int>();
Console.WriteLine("Write something : ");
var input = Console.ReadLine();
int index;
foreach (var vowel in vowels)
{
if (input.Contains(vowel))
{
index = input.IndexOf(vowel);
indexes.Add(index + 1);
chars.Add(vowel);
}
}
Consider this approach:
using System;
using System.Linq;
using System.Collections.Generic;
namespace Whatever
{
class Program
{
static void Main(string[] args)
{
var vowels = new Dictionary<string, int>(5, StringComparer.OrdinalIgnoreCase) { { "a", 1 }, { "e", 5 }, { "i", 9 }, { "o", 15 }, { "u", 21 } };
Console.WriteLine("Write something : ");
var input = Console.ReadLine();
var sum = input.Select((value, index) => new { value, index })
.Sum(x =>
{
vowels.TryGetValue(x.value.ToString(), out var multiplier);
return (x.index + 1) * multiplier;
});
Console.ReadLine();
}
}
}
The Select projects the original string as an anonymous type with the char and its index included.
The Sum checks if the string is a vowel - and if it is it multiplies the position (index + 1) by the position in the alphabet (from vowels).
vowels is case insensitive so that "A" and "a" are treated the same.
If the compiler complains about the out var then use:
int multiplier = 0;
vowels.TryGetValue(x.value.ToString(), out multiplier);
return (x.index + 1) * multiplier;
instead.
i figured it out right here
for (int i = 0; i < indexes.Count; i++)
{
sumofone += indexes[i] * (char.ToUpper(chars[i]) - 64);
}
You can do this (Reference is from here):
var vowels = new char[] { 'a', 'e', 'i', 'o', 'u' };
Console.WriteLine("Write something : ");
var input = Console.ReadLine().ToLower();
int total = 0;
for (int temp = 1; temp <= input.Length; temp++)
{
if (vowels.Contains(input[temp - 1]))
{
total += temp * (char.ToUpper(input[temp -1]) - 64);
}
}
Console.WriteLine("The length is " + total);
I am using the following function to convert numbers to text:
public static string NumberToText(long number)
{
StringBuilder wordNumber = new StringBuilder();
string[] powers = new string[] { "Thousand ", "Million ", "Billion " };
string[] tens = new string[] { "Twenty", "Thirty", "Forty", "Fifty", "Sixty", "Seventy", "Eighty", "Ninety" };
string[] ones = new string[] { "One", "Two", "Three", "Four", "Five", "Six", "Seven", "Eight", "Nine", "Ten",
"Eleven", "Twelve", "Thirteen", "Fourteen", "Fifteen", "Sixteen", "Seventeen", "Eighteen", "Nineteen" };
if (number == 0) { return "Zero"; }
if (number < 0)
{
wordNumber.Append("Negative ");
number = -number;
}
long[] groupedNumber = new long[] { 0, 0, 0, 0 };
int groupIndex = 0;
while (number > 0)
{
groupedNumber[groupIndex++] = number % 1000;
number /= 1000;
}
for (int i = 3; i >= 0; i--)
{
long group = groupedNumber[i];
if (group >= 100)
{
wordNumber.Append(ones[group / 100 - 1] + " Hundred ");
group %= 100;
if (group == 0 && i > 0)
wordNumber.Append(powers[i - 1]);
}
if (group >= 20)
{
if ((group % 10) != 0)
wordNumber.Append(tens[group / 10 - 2] + " " + ones[group % 10 - 1] + " ");
else
wordNumber.Append(tens[group / 10 - 2] + " ");
}
else if (group > 0)
wordNumber.Append(ones[group - 1] + " ");
if (group != 0 && i > 0)
wordNumber.Append(powers[i - 1]);
}
return wordNumber.ToString().Trim();
}
This works fine.
The issue is that the returned text is too long to read. I want to convert it to something shorter.
For eg: 345435234 is returned as three hundred and forty-five million, four hundred and thirty-five thousand, two hundred and thirty-four.
I instead would like 345.4 Mil.
Here are some more examples:
3454 should be 3.4K
34543 should be 34.5K
345433 should be 345.4K
... and so on.
How do I achieve this?
Maybe something like this?
Though if you have "printable character OCD" you could probably get it smaller
private static string[] suffixes = new[] { "", "K", "Mill", "Bill", "Trill", "Zill" };
public static string ToStuff(double number, int precision = 2)
{
const double unit = 1000;
var i = 0;
while (number > unit)
{
number /= unit;
i++;
}
if(i >= 5) throw new Exception("No one can count this high");
return Math.Round(number, precision, MidpointRounding.AwayFromZero) + suffixes[i];
}
Disclaimer : Totally untested
Update
Due to popular demand, i tested it with a test case of 1 E.g 2500000, 0 and it output 2Mill. I think i might have a career in this counting game
Update 2
Due to more popular demand, the consensus is that we shouldn't use .Net default rounding ToEven (bankers rounding) , and should use AwayFromZero
Try the following:
if (number>1000000)
string = floor(number/1000000).ToString() + "m";
else if (number > 1000)
string = floor(number/1000).ToString() + "k";
else
string = number.ToString();
First I've to say that's I'm not asking, how to convert integer value to the string phrase.
My question is how to get each number exist between words in string as separate value to replace 46 000 000 with forty-six million and 70 000 000 with seventy million. To make it easier and not confuse for example if string value already exists:
string val1 = "replace1";
string val2 = "replace2";
and input string:
string inputStr = "distance from the Sun ranging from 46 000 000 to 70 000 000 km";
to get this result:
distance from the Sun ranging from replace1 to replace2 km
I'm not asking how to replace value to other value, my question is how to extract exist numbers from string as separate values to replace it after.
string foundNum1 = "46 000 000";
string foundNum2 = "70 000 000";
Only then I can use replace method:
string f1 = inputStr.Replace(foundNum1, val1);
string outStr = f1.Replace(foundNum2, val2);
Here is a complete answer for any number you need to convert:
public static string NumberToWords(int number)
{
if (number == 0)
return "zero";
if (number < 0)
return "minus " + NumberToWords(Math.Abs(number));
string words = "";
if ((number / 1000000) > 0)
{
words += NumberToWords(number / 1000000) + " million ";
number %= 1000000;
}
if ((number / 1000) > 0)
{
words += NumberToWords(number / 1000) + " thousand ";
number %= 1000;
}
if ((number / 100) > 0)
{
words += NumberToWords(number / 100) + " hundred ";
number %= 100;
}
if (number > 0)
{
if (words != "")
words += "and ";
var unitsMap = new[] { "zero", "one", "two", "three", "four", "five", "six", "seven", "eight", "nine", "ten", "eleven", "twelve", "thirteen", "fourteen", "fifteen", "sixteen", "seventeen", "eighteen", "nineteen" };
var tensMap = new[] { "zero", "ten", "twenty", "thirty", "forty", "fifty", "sixty", "seventy", "eighty", "ninety" };
if (number < 20)
words += unitsMap[number];
else
{
words += tensMap[number / 10];
if ((number % 10) > 0)
words += "-" + unitsMap[number % 10];
}
}
return words;
}
public string ConvertStringNumbersToWrittenForm(string inputStr)
{
var regex = new Regex(#"[\d][\d ]+[\d]");
var matches = regex.Matches(inputStr);
string newString = inputStr;
foreach (var match in matches)
{
string matchStr = match.ToString();
//eliminate spaces
matchStr = matchStr.Replace(" ", string.Empty);
newString = newString.Replace(match.ToString(), NumberToWords(Convert.ToInt32(matchStr)));
}
return newString;
}
To use it, you call:
string wordString = ConvertStringNumbersToWrittenForm("distance from the Sun ranging from 46 000 000 to 70 000 000 km");
and wordString will be the resulting string, with written words for the numbers.
To do this dynamically, you could use a regex like follows:
string val1 = "replace1";
string val2 = "replace2";
string inputStr = "distance from the Sun ranging from 46 000 000 to 70 000 000 km";
var regex = new Regex(#"[\d][\d ]+[\d]");
var matches = regex.Matches(inputStr);
inputStr.Replace(matches[0], val1);
inputStr.Replace(matches[1], val2);
Just note the following:
if there are not two matches, this won't wokr
if the matches are the same, this will always replace both
there are probably other edge cases
This will get you back any group of numbers.
string inputStr = "distance from the Sun ranging from 46 000 000 to 70 000 000 km";
Regex pattern = new Regex(#"([0-9]{1,3}(?:\s[0-9]{3})*)");
foreach (Match match in pattern.Matches(inputStr))
{
Console.WriteLine(match.Groups[1]);
}
This displays each match. Now you would just need to do your replace the way you want. Not sure what you are planning there; that's why I didn't put anything in place.
You can do something like this:
string res = inputStr.Replace("46 000 000", val1).Replace("70 000 000", val2);
I'm pretty sure you do it like this:
inputStr = inputStr.Replace("46 000 000", "forty six million");
inputStr = inputStr.Replace("70 000 000 ", "seventy million");
This question already has answers here:
Closed 12 years ago.
Possible Duplicate:
How can I convert an integer into its verbal representation?
Can anybody give me a primer code I could work on in converting numbers into words?
Converting numbers to words (ranging from -1000 to +1000)
example: 1000 --> one thousand
public static string NumberToWords(int number)
{
if (number == 0)
return "zero";
if (number < 0)
return "minus " + NumberToWords(Math.Abs(number));
string words = "";
if ((number / 1000000) > 0)
{
words += NumberToWords(number / 1000000) + " million ";
number %= 1000000;
}
if ((number / 1000) > 0)
{
words += NumberToWords(number / 1000) + " thousand ";
number %= 1000;
}
if ((number / 100) > 0)
{
words += NumberToWords(number / 100) + " hundred ";
number %= 100;
}
if (number > 0)
{
if (words != "")
words += "and ";
var unitsMap = new[] { "zero", "one", "two", "three", "four", "five", "six", "seven", "eight", "nine", "ten", "eleven", "twelve", "thirteen", "fourteen", "fifteen", "sixteen", "seventeen", "eighteen", "nineteen" };
var tensMap = new[] { "zero", "ten", "twenty", "thirty", "forty", "fifty", "sixty", "seventy", "eighty", "ninety" };
if (number < 20)
words += unitsMap[number];
else
{
words += tensMap[number / 10];
if ((number % 10) > 0)
words += "-" + unitsMap[number % 10];
}
}
return words;
}
When I had to solve this problem, I created a hard-coded data dictionary to map between numbers and their associated words. For example, the following might represent a few entries in the dictionary:
{1, "one"}
{2, "two"}
{30, "thirty"}
You really only need to worry about mapping numbers in the 10^0 (1,2,3, etc.) and 10^1 (10,20,30) positions because once you get to 100, you simply have to know when to use words like hundred, thousand, million, etc. in combination with your map. For example, when you have a number like 3,240,123, you get: three million two hundred forty thousand one hundred twenty three.
After you build your map, you need to work through each digit in your number and figure out the appropriate nomenclature to go with it.