How to convert number to english?

Hi gurus,

I have a weird requirement. I need to convert the number to english lecture.

I have 1.2 ....19 numbers
I need to convert to first second third fourth, fifth, sixth...

Is there any way convert it using unix command?

thanks in advance.

Some more context, please. Do you want ordinal (as posted) or cardinal numbers? What tool/system/language do you need this for? Show input an desired output.

Quick'n'dirty...
Longhand using OSX 10.7.5, default bash terminal.

#!/bin/bash
# numalpha.sh
alpha=""
n=1
numalpha()
{
	if [ $n -eq 1 ]
	then
		alpha="first"
	fi
	if [ $n -eq 2 ]
	then
		alpha="second"
	fi
	if [ $n -eq 3 ]
	then
		alpha="third"
	fi
	if [ $n -eq 4 ]
	then
		alpha="fourth"
	fi
	# And so on...
}
# Test for integers numbers only.
for n in {1..4}
do
	numalpha
	echo "$n = $alpha"
done

Results for 4 values only.

Last login: Sat Feb 14 21:06:21 on ttys000
AMIGA:barrywalker~> cd Desktop/Code/Shell
AMIGA:barrywalker~/Desktop/Code/Shell> chmod 755 numalpha.sh
AMIGA:barrywalker~/Desktop/Code/Shell> ./numalpha.sh
1 = first
2 = second
3 = third
4 = fourth
AMIGA:barrywalker~/Desktop/Code/Shell> _

Hi,
With bash, it's more easy to use array,example ($ in begin line is my prompt):

$ nblexical=("zero" "first" "second" "third")
$ nb=2
$ echo ${nblexical[$nb]} 
second

Regard.

Same machine...

#!/bin/bash
# numalpha.sh
alpha=""
numalpha()
{
	if [ $1 -eq 1 ]
	then
		alpha="first"
	fi
	if [ $1 -eq 2 ]
	then
		alpha="second"
	fi
	if [ $1 -eq 3 ]
	then
		alpha="third"
	fi
	if [ $1 -eq 4 ]
	then
		alpha="fourth"
	fi
	# And so on...
}
# Test for integer numbers only.
text=( This is the 1 and only attempt to change the 4 word inside this string. )
count=0
while [ $count -lt ${#text[@]} ]
do
	if [[ ${text[$count]} =~ ^-?[0-9]+$ ]]
	then
		numalpha "${text[$count]}"
		printf "$alpha "
		count=$((count+1))
	fi
	printf "${text[$count]} "
	alpha=""
	count=$((count+1))
done

Results:-

Last login: Sat Feb 14 22:54:01 on ttys000
AMIGA:barrywalker~> cd Desktop/Code/Shell
AMIGA:barrywalker~/Desktop/Code/Shell> ./numalpha.sh
This is the first and only attempt to change the fourth word inside this string.
AMIGA:barrywalker~/Desktop/Code/Shell> _

For a US English translation of the integers in the range from 0 up to and including 999999999999999999999999999999999999 into ordinal numbers, you could try something like:

#!/bin/ksh
awk '
BEGIN {	# Initialize variables...
	# Final single digits:
	fd[0] = "zeroth"; fd[1] = "first"; fd[2] = "second"; fd[3] = "third"
	fd[4] = "fourth"; fd[5] = "fifth"; fd[6] = "sixth"; fd[7] = "seventh"
	fd[8] = "eighth"; fd[9] = "ninth"
	# Leading single digits:
	ld[1] = "one"; ld[2] = "two"; ld[3] = "three"; ld[4] = "four"
	ld[5] = "five"; ld[6] = "six"; ld[7] = "seven"; ld[8] = "eight"
	ld[9] = "nine"
	# Final teens:
	ft[10] = "tenth"; ft[11] = "eleventh"; ft[12] = "twelfth"
	ft[13] = "thirteenth"; ft[14] = "fourteenth"; ft[15] = "fifteenth"
	ft[16] = "sixteenth"; ft[17] = "seventeenth"; ft[18] = "eighteenth"
	ft[19] = "nineteenth"
	# Leading teens:
	lt[10] = "ten"; lt[11] = "eleven"; lt[12] = "twelve"
	lt[13] = "thirteen"; lt[14] = "fourteen"; lt[15] = "fifteen"
	lt[16] = "sixteen"; lt[17] = "seventeen"; lt[18] = "eighteen"
	lt[19] = "nineteen"
	# Final tens:
	fT[2] = "twentieth"; fT[3] = "thirtieth"; fT[4] = "fortieth"
	fT[5] = "fiftieth"; fT[6] = "sixtieth"; fT[7] - "seventieth"
	fT[8] = "eightieth"; fT[9] = "ninetieth"
	# Leading tens:
	lT[2] = "twenty"; lT[3] = "thirty"; lT[4] = "forty"; lT[5] = "fifty"
	lT[6] = "sixty"; lT[7] = "seventy"; lT[8] = "eighty"; lT[9] = "ninety"
	# Units:
	u[2] = "thousand"; u[3] = "million"; u[4] = "billion"; u[5] = "trillion"
	u[6] = "quadrillion"; u[7] = "quintillion"; u[8] = "sextillion"
	u[9] = "septillion"; u[10] = "octillion"; u[11] = "nonillion"
	u[12] = "decillion"; u[13] = "undecillion"
	# The last entry above will only be used in overflow diagnostics.  If
	# more entries are added, remember that one extra entry must be added.
	# The following maximum u[] subscript must be updated if more entries
	# are added above.
	ucnt = 13
}
# Function to print US English string corresponding to 3 digit numeric string.
function p3(units, gcnt, gnum,    d1, d2, d3, d23) {
	# If we have a zero and this is not the last group, nothing to print...
	if(g[gnum] == 0 && gnum < gcnt) return(1)
	# Grab inividual digits and last two digits...
	d1 = int(g[gnum] / 100)
	d23 = g[gnum] % 100
	d2 = int(d23 / 10)
	d3 = d23 % 10
	# Hundreds to print?
	if(d1)
		printf("%s hundred%s", ld[d1],
			d23 ? " " : (gcnt == gnum) ? "th\n" : " " units \
				(t[gnum] ? " " : "th\n"))
	# Print last two digits...
	if(d23 || (d1 == 0 && gnum == gcnt))
		if(d2 == 1) 
			# 10-19:
			printf("%s", (gnum == gcnt) ? ft[d23] "\n" : \
				lt[d23] " " units (t[gnum] ? " " : "th\n"))
		else if(d2)
			# 20-99:
			if(d3)	# [2-9][1-9]:
				printf("%s-%s", lT[d2],
					(gnum == gcnt) ? fd[d3] "\n" : \
					ld[d3] " " units \
					(t[gnum] ? " " : "th\n"))
			else	# [2-9]0:
				printf("%s", (gnum == gcnt) ? fT[d2] "\n" : \
					lT[d2] " " units \
					(t[gnum] ? " " : "th\n"))
		else	# 0-9:
			printf("%s", (gnum == gcnt) ? fd[d3] "\n" : ld[d3] " " \
				units (t[gnum] ? " " : "th\n"))
	return(t[gnum])
}
# Process the first field from each line in an input file...
{	# Show original input...
	printf("Input:\"%s\"\n", $0)
	# Check for non-digits
	if(match($1, /[^[:digit:]]/)) {
		print "Only digits are alloweed."
		next
	}
	# Strip leading 0s
	if(match($1, /^0+/)) {
		$1 = (RLENGTH == length($1)) ? "0" : substr($1, RLENGTH + 1)
		printf("Updated input:\"%s\"\n", $0)
	}
	# Split into groups of three digits...
	ng = int((length($1) + 2) / 3)
	if(ng == 0) next	# skip eimpty lines
	if(ng >= ucnt) {	# Too big to handle?
		printf("Can only handle numbers less than one %s.\n",
			u[ucnt])
		next
	}
	gw = length($1) - (ng - 1) * 3
	off = 1
	for(i = 1; i <= ng; i++) {
		g = substr($1, off, gw)
		t = substr($1, off + gw) + 0
		off += gw
		gw = 3
	}
	# Process the groups of digits...
	for(i = 1; p3(u[ng + 1 - i], ng, i); i++);
}' file

This awk script will only convert the 1st field in each line read from a file named file . You can easily change this to read from standard input or another file and, without too much work, it could process a different set of fields from each input line.

If you want to try this on a Solaris/SunOS system, change awk to /usr/xpg4/bin/awk , /usr/xpg6/bin/awk , or nawk . This was written and tested on OS X 10.10.2.

With file containing:

0
1
11
111
123000000456000000789
1000000000000000000000023
2034500
975310000000000000000000000987654321
1000000000000000000000000000000000000

the output produced is:

Input:"0"
Updated input:"0"
zeroth
Input:"1"
first
Input:"11"
eleventh
Input:"111"
one hundred eleventh
Input:"123000000456000000789"
one hundred twenty-three quintillion four hundred fifty-six billion seven hundred eighty-ninth
Input:"1000000000000000000000023"
one septillion twenty-third
Input:"2034500"
two million thirty-four thousand five hundredth
Input:"975310000000000000000000000987654321"
nine hundred seventy-five decillion three hundred ten nonillion nine hundred eighty-seven million six hundred fifty-four thousand three hundred twenty-first
Input:"1000000000000000000000000000000000000"
Can only handle numbers less than one undecillion.

If you need to extend the range of numbers accepted, you can find higher order names here: Wikipedia: Names of large numbers.

8 Likes

Hi.

For Don's data file, augmented, in file data2:"

0
1
11
111
1,011
1_012
123000000456000000789
1000000000000000000000023
2034500
975310000000000000000000000987654321
1000000000000000000000000000000000000

the perl code:

perl -M"Lingua::EN::Numbers qw(num2en num2en_ordinal)" -n -e 'chomp;
print " Cardinal for $_: ",num2en($_),"\n";
print " Ordinal  for $_: ", num2en_ordinal($_),"\n";' data2

produces:

 Cardinal for 0: zero
 Ordinal  for 0: zeroth
 Cardinal for 1: one
 Ordinal  for 1: first
 Cardinal for 11: eleven
 Ordinal  for 11: eleventh
 Cardinal for 111: one hundred and eleven
 Ordinal  for 111: one hundred and eleventh
 Cardinal for 1,011: one thousand and eleven
 Ordinal  for 1,011: one thousand and eleventh
 Cardinal for 1_012: 
 Ordinal  for 1_012: 
 Cardinal for 123000000456000000789: one hundred and twenty-three quintillion, four hundred and fifty-six billion, seven hundred and eighty-nine
 Ordinal  for 123000000456000000789: one hundred and twenty-three quintillion, four hundred and fifty-six billion, seven hundred and eighty-ninth
 Cardinal for 1000000000000000000000023: one septillion and twenty-three
 Ordinal  for 1000000000000000000000023: one septillion and twenty-third
 Cardinal for 2034500: two million, thirty-four thousand, five hundred
 Ordinal  for 2034500: two million, thirty-four thousand, five hundredth
 Cardinal for 975310000000000000000000000987654321: nine hundred and seventy-five times ten to the thirty-third, three hundred and ten nonillion, nine hundred and eighty-seven million, six hundred and fifty-four thousand, three hundred and twenty-one
 Ordinal  for 975310000000000000000000000987654321: nine hundred and seventy-five times ten to the thirty-third, three hundred and ten nonillion, nine hundred and eighty-seven million, six hundred and fifty-four thousand, three hundred and twenty-first
 Cardinal for 1000000000000000000000000000000000000: one times ten to the thirty-sixth
 Ordinal  for 1000000000000000000000000000000000000: one times ten to the thirty-sixthth

As noted in How can I convert a number to its English form in Perl? - Stack Overflow

Best wishes ... cheers, drl

While Don Cragun put in an impressive amount of work, my first thought when I saw the problem was that there is probably a Perl module for that.

Lingua::EN::Nums2Words - search.cpan.org

I don't know if it works, but you should be able to download the above Perl module using CPAN and probably find a pre-written script that will do the heavy lifting. The great part about using pre-built modules is that someone else is already maintaining the code.

Sorry DRL, I reread the post and realized that you already posted the same answer. It's worth noting that Lingua::EN::Numbers is probably not installed by default.

1 Like

Hi, gandolf989.

Yes, good point. Thanks for making that comment ... cheers, drl