Skip to content

Commit f195204

Browse files
committed
Add solutions for Ch08
1 parent ec2de4e commit f195204

27 files changed

+1297
-0
lines changed
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
<Project Sdk="Microsoft.NET.Sdk">
2+
3+
<PropertyGroup>
4+
<OutputType>Exe</OutputType>
5+
<TargetFramework>net8.0</TargetFramework>
6+
<ImplicitUsings>enable</ImplicitUsings>
7+
<Nullable>enable</Nullable>
8+
</PropertyGroup>
9+
10+
<ItemGroup>
11+
<Using Include="System.Console" Static="true" />
12+
</ItemGroup>
13+
14+
</Project>
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
using System.Text.RegularExpressions;
2+
3+
WriteLine("The default regular expression checks for at least one digit.");
4+
5+
do
6+
{
7+
Write("Enter a regular expression (or press ENTER to use the default): ");
8+
string? regexp = ReadLine();
9+
10+
if (string.IsNullOrWhiteSpace(regexp))
11+
{
12+
regexp = @"^\d+$";
13+
}
14+
15+
Write("Enter some input: ");
16+
string input = ReadLine()!; // will never be null
17+
18+
Regex r = new(regexp);
19+
20+
WriteLine($"{input} matches {regexp}: {r.IsMatch(input)}");
21+
22+
WriteLine("Press ESC to end or any key to try again.");
23+
}
24+
while (ReadKey(intercept: true).Key != ConsoleKey.Escape);
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
<Project Sdk="Microsoft.NET.Sdk">
2+
3+
<PropertyGroup>
4+
<OutputType>Exe</OutputType>
5+
<TargetFramework>net8.0</TargetFramework>
6+
<ImplicitUsings>enable</ImplicitUsings>
7+
<Nullable>enable</Nullable>
8+
</PropertyGroup>
9+
10+
<ItemGroup>
11+
<ProjectReference Include="..\Ch08Ex03NumbersAsWordsLib\Ch08Ex03NumbersAsWordsLib.csproj" />
12+
</ItemGroup>
13+
14+
<ItemGroup>
15+
<Using Include="System.Console" Static="true" />
16+
</ItemGroup>
17+
18+
</Project>
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
using Packt.Shared; // ToWords extension method
2+
using System.Numerics; // BigInteger
3+
4+
Write("Enter a number up to twenty one digits long: ");
5+
string? input = ReadLine();
6+
if (input is null) return;
7+
8+
if (input.Length > 21)
9+
{
10+
WriteLine("I cannot handle more than twenty one digits!");
11+
return;
12+
}
13+
14+
BigInteger number = BigInteger.Parse(input);
15+
16+
WriteLine($"{number:N0} in words is {number.ToWords()}.");
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
<Project Sdk="Microsoft.NET.Sdk">
2+
3+
<PropertyGroup>
4+
<TargetFramework>netstandard2.0</TargetFramework>
5+
</PropertyGroup>
6+
7+
</Project>
Lines changed: 220 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,220 @@
1+
using System.Diagnostics; // Trace
2+
using System.IO; // File
3+
using System.Numerics; // BigInteger
4+
5+
namespace Packt.Shared
6+
{
7+
public static class NumbersToWords
8+
{
9+
// Single-digit and small number names
10+
private static string[] smallNumbers = new string[]
11+
{
12+
"zero", "one", "two", "three", "four", "five", "six", "seven", "eight",
13+
"nine", "ten", "eleven", "twelve", "thirteen", "fourteen", "fifteen",
14+
"sixteen", "seventeen", "eighteen", "nineteen"
15+
};
16+
17+
// Tens number names from twenty upwards
18+
private static string[] tens = new string[]
19+
{
20+
"", "", "twenty", "thirty", "forty", "fifty", "sixty", "seventy",
21+
"eighty", "ninety"
22+
};
23+
24+
// Scale number names for use during recombination
25+
private static string[] scaleNumbers = new string[]
26+
{
27+
"", "thousand", "million", "billion", "trillion",
28+
"quadrillion", "quintillion"
29+
};
30+
31+
private static int groups = 7; // i.e. up to quintillion
32+
33+
public static string ToWords(this int number)
34+
{
35+
return ToWords((BigInteger)number);
36+
}
37+
38+
public static string ToWords(this long number)
39+
{
40+
return ToWords((BigInteger)number);
41+
}
42+
43+
public static string ToWords(this BigInteger number)
44+
{
45+
/*
46+
Convert A Number into Words
47+
by Richard Carr, published at http://www.blackwasp.co.uk/numbertowords.aspx
48+
*/
49+
50+
/*
51+
Zero Rule.
52+
If the value is 0 then the number in words is 'zero' and no other rules apply.
53+
*/
54+
if (number == 0)
55+
{
56+
return "zero";
57+
}
58+
59+
/*
60+
Three Digit Rule.
61+
The integer value is split into groups of three digits starting from the
62+
right-hand side. Each set of three digits is then processed individually
63+
as a number of hundreds, tens and units. Once converted to text, the
64+
three-digit groups are recombined with the addition of the relevant scale
65+
number (thousand, million, billion).
66+
*/
67+
68+
// Array to hold the specified number of three-digit groups
69+
int[] digitGroups = new int[groups];
70+
71+
// Ensure a positive number to extract from
72+
var positive = BigInteger.Abs(number);
73+
74+
// Extract the three-digit groups
75+
for (int i = 0; i < groups; i++)
76+
{
77+
digitGroups[i] = (int)(positive % 1000);
78+
positive /= 1000;
79+
}
80+
81+
// write to a text file in the project folder
82+
Trace.Listeners.Add(new TextWriterTraceListener(
83+
File.AppendText("log.txt")));
84+
85+
// text writer is buffered, so this option calls
86+
// Flush() on all listeners after writing
87+
Trace.AutoFlush = true;
88+
89+
// log array of group numbers
90+
for (int x = 0; x < digitGroups.Length; x++)
91+
{
92+
Trace.WriteLine(string.Format(
93+
format: "digitGroups[{0}] = {1}",
94+
arg0: x,
95+
arg1: digitGroups[x]));
96+
}
97+
98+
// Convert each three-digit group to words
99+
string[] groupTexts = new string[groups];
100+
101+
for (int i = 0; i < groups; i++)
102+
{
103+
// call a local function (see below)
104+
groupTexts[i] = ThreeDigitGroupToWords(digitGroups[i]);
105+
}
106+
107+
// log array of group texts
108+
for (int x = 0; x < groupTexts.Length; x++)
109+
{
110+
Trace.WriteLine(string.Format(
111+
format: "groupTexts[{0}] = {1}",
112+
arg0: x,
113+
arg1: groupTexts[x]));
114+
}
115+
116+
/*
117+
Recombination Rules.
118+
When recombining the translated three-digit groups, each group except the
119+
last is followed by a large number name and a comma, unless the group is
120+
blank and therefore not included at all. One exception is when the final
121+
group does not include any hundreds and there is more than one non-blank
122+
group. In this case, the final comma is replaced with 'and'. eg.
123+
'one billion, one million and twelve'.
124+
*/
125+
126+
// Recombine the three-digit groups
127+
string combined = groupTexts[0];
128+
bool appendAnd;
129+
130+
// Determine whether an 'and' is needed
131+
appendAnd = (digitGroups[0] > 0) && (digitGroups[0] < 100);
132+
133+
// Process the remaining groups in turn, smallest to largest
134+
for (int i = 1; i < groups; i++)
135+
{
136+
// Only add non-zero items
137+
if (digitGroups[i] != 0)
138+
{
139+
// Build the string to add as a prefix
140+
string prefix = groupTexts[i] + " " + scaleNumbers[i];
141+
142+
if (combined.Length != 0)
143+
{
144+
prefix += appendAnd ? " and " : ", ";
145+
}
146+
147+
// Opportunity to add 'and' is ended
148+
appendAnd = false;
149+
150+
// Add the three-digit group to the combined string
151+
combined = prefix + combined;
152+
}
153+
}
154+
155+
// Converts a three-digit group into English words
156+
string ThreeDigitGroupToWords(int threeDigits)
157+
{
158+
// Initialise the return text
159+
string groupText = "";
160+
161+
// Determine the hundreds and the remainder
162+
int hundreds = threeDigits / 100;
163+
int tensUnits = threeDigits % 100;
164+
165+
/*
166+
Hundreds Rules.
167+
If the hundreds portion of a three-digit group is not zero, the number of
168+
hundreds is added as a word. If the three-digit group is exactly divisible
169+
by one hundred, the text 'hundred' is appended. If not, the text
170+
"hundred and" is appended. eg. 'two hundred' or 'one hundred and twelve'
171+
*/
172+
173+
if (hundreds != 0)
174+
{
175+
groupText += smallNumbers[hundreds] + " hundred";
176+
177+
if (tensUnits != 0)
178+
{
179+
groupText += " and ";
180+
}
181+
}
182+
183+
// Determine the tens and units
184+
int tens = tensUnits / 10;
185+
int units = tensUnits % 10;
186+
187+
/* Tens Rules.
188+
If the tens section of a three-digit group is two or higher, the appropriate
189+
'-ty' word (twenty, thirty, etc.) is added to the text and followed by the
190+
name of the third digit (unless the third digit is a zero, which is ignored).
191+
If the tens and the units are both zero, no text is added. For any other value,
192+
the name of the one or two-digit number is added as a special case.
193+
*/
194+
195+
if (tens >= 2)
196+
{
197+
groupText += NumbersToWords.tens[tens];
198+
if (units != 0)
199+
{
200+
groupText += " " + smallNumbers[units];
201+
}
202+
}
203+
else if (tensUnits != 0)
204+
groupText += smallNumbers[tensUnits];
205+
206+
return groupText;
207+
}
208+
209+
/* Negative Rule.
210+
Negative numbers are always preceded by the text 'negative'.
211+
*/
212+
if (number < 0)
213+
{
214+
combined = "negative " + combined;
215+
}
216+
217+
return combined;
218+
}
219+
}
220+
}
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
<Project Sdk="Microsoft.NET.Sdk">
2+
3+
<PropertyGroup>
4+
<TargetFramework>net8.0</TargetFramework>
5+
<ImplicitUsings>enable</ImplicitUsings>
6+
<Nullable>enable</Nullable>
7+
8+
<IsPackable>false</IsPackable>
9+
</PropertyGroup>
10+
11+
<ItemGroup>
12+
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.7.0-*" />
13+
<PackageReference Include="xunit" Version="2.5.0" />
14+
<PackageReference Include="xunit.runner.visualstudio" Version="2.5.0">
15+
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
16+
<PrivateAssets>all</PrivateAssets>
17+
</PackageReference>
18+
<PackageReference Include="coverlet.collector" Version="6.0.0">
19+
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
20+
<PrivateAssets>all</PrivateAssets>
21+
</PackageReference>
22+
</ItemGroup>
23+
24+
<ItemGroup>
25+
<ProjectReference Include="..\Ch08Ex03NumbersAsWordsLib\Ch08Ex03NumbersAsWordsLib.csproj" />
26+
</ItemGroup>
27+
28+
</Project>

0 commit comments

Comments
 (0)