diff --git a/vs4win/Chapter08/Ch08Ex02RegularExpressions/Ch08Ex02RegularExpressions.csproj b/vs4win/Chapter08/Ch08Ex02RegularExpressions/Ch08Ex02RegularExpressions.csproj new file mode 100644 index 0000000..6431db6 --- /dev/null +++ b/vs4win/Chapter08/Ch08Ex02RegularExpressions/Ch08Ex02RegularExpressions.csproj @@ -0,0 +1,14 @@ + + + + Exe + net7.0 + enable + enable + + + + + + + diff --git a/vs4win/Chapter08/Ch08Ex02RegularExpressions/Program.cs b/vs4win/Chapter08/Ch08Ex02RegularExpressions/Program.cs new file mode 100644 index 0000000..118f186 --- /dev/null +++ b/vs4win/Chapter08/Ch08Ex02RegularExpressions/Program.cs @@ -0,0 +1,24 @@ +using System.Text.RegularExpressions; + +WriteLine("The default regular expression checks for at least one digit."); + +do +{ + Write("Enter a regular expression (or press ENTER to use the default): "); + string? regexp = ReadLine(); + + if (string.IsNullOrWhiteSpace(regexp)) + { + regexp = @"^\d+$"; + } + + Write("Enter some input: "); + string input = ReadLine()!; // will never be null + + Regex r = new(regexp); + + WriteLine($"{input} matches {regexp}: {r.IsMatch(input)}"); + + WriteLine("Press ESC to end or any key to try again."); +} +while (ReadKey(intercept: true).Key != ConsoleKey.Escape); \ No newline at end of file diff --git a/vs4win/Chapter08/Ch08Ex03NumbersAsWordsApp/Ch08Ex03NumbersAsWordsApp.csproj b/vs4win/Chapter08/Ch08Ex03NumbersAsWordsApp/Ch08Ex03NumbersAsWordsApp.csproj new file mode 100644 index 0000000..c91be9d --- /dev/null +++ b/vs4win/Chapter08/Ch08Ex03NumbersAsWordsApp/Ch08Ex03NumbersAsWordsApp.csproj @@ -0,0 +1,18 @@ + + + + Exe + net7.0 + enable + enable + + + + + + + + + + + diff --git a/vs4win/Chapter08/Ch08Ex03NumbersAsWordsApp/Program.cs b/vs4win/Chapter08/Ch08Ex03NumbersAsWordsApp/Program.cs new file mode 100644 index 0000000..7caac9e --- /dev/null +++ b/vs4win/Chapter08/Ch08Ex03NumbersAsWordsApp/Program.cs @@ -0,0 +1,16 @@ +using Packt.Shared; // ToWords extension method +using System.Numerics; // BigInteger + +Write("Enter a number up to twenty one digits long: "); +string? input = ReadLine(); +if (input is null) return; + +if (input.Length > 21) +{ + WriteLine("I cannot handle more than twenty one digits!"); + return; +} + +BigInteger number = BigInteger.Parse(input); + +WriteLine($"{number:N0} in words is {number.ToWords()}."); \ No newline at end of file diff --git a/vs4win/Chapter08/Ch08Ex03NumbersAsWordsLib/Ch08Ex03NumbersAsWordsLib.csproj b/vs4win/Chapter08/Ch08Ex03NumbersAsWordsLib/Ch08Ex03NumbersAsWordsLib.csproj new file mode 100644 index 0000000..9f5c4f4 --- /dev/null +++ b/vs4win/Chapter08/Ch08Ex03NumbersAsWordsLib/Ch08Ex03NumbersAsWordsLib.csproj @@ -0,0 +1,7 @@ + + + + netstandard2.0 + + + diff --git a/vs4win/Chapter08/Ch08Ex03NumbersAsWordsLib/NumbersToWords.cs b/vs4win/Chapter08/Ch08Ex03NumbersAsWordsLib/NumbersToWords.cs new file mode 100644 index 0000000..5ea0834 --- /dev/null +++ b/vs4win/Chapter08/Ch08Ex03NumbersAsWordsLib/NumbersToWords.cs @@ -0,0 +1,220 @@ +using System.Diagnostics; // Trace +using System.IO; // File +using System.Numerics; // BigInteger + +namespace Packt.Shared +{ + public static class NumbersToWords + { + // 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" + }; + + private static int groups = 7; // i.e. up to quintillion + + public static string ToWords(this int number) + { + return ToWords((BigInteger)number); + } + + public static string ToWords(this long number) + { + return ToWords((BigInteger)number); + } + + public static string ToWords(this BigInteger number) + { + /* + Convert A Number into Words + by Richard Carr, published at http://www.blackwasp.co.uk/numbertowords.aspx + */ + + /* + Zero Rule. + If the value is 0 then the number in words is 'zero' and no other rules apply. + */ + if (number == 0) + { + return "zero"; + } + + /* + Three Digit Rule. + The integer value is split into groups of three digits starting from the + right-hand side. Each set of three digits is then processed individually + as a number of hundreds, tens and units. Once converted to text, the + three-digit groups are recombined with the addition of the relevant scale + number (thousand, million, billion). + */ + + // Array to hold the specified number of three-digit groups + 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; + } + + // write to a text file in the project folder + Trace.Listeners.Add(new TextWriterTraceListener( + File.AppendText("log.txt"))); + + // text writer is buffered, so this option calls + // Flush() on all listeners after writing + Trace.AutoFlush = true; + + // log array of group numbers + for (int x = 0; x < digitGroups.Length; x++) + { + Trace.WriteLine(string.Format( + format: "digitGroups[{0}] = {1}", + arg0: x, + arg1: digitGroups[x])); + } + + // Convert each three-digit group to words + string[] groupTexts = new string[groups]; + + for (int i = 0; i < groups; i++) + { + // call a local function (see below) + groupTexts[i] = ThreeDigitGroupToWords(digitGroups[i]); + } + + // log array of group texts + for (int x = 0; x < groupTexts.Length; x++) + { + Trace.WriteLine(string.Format( + format: "groupTexts[{0}] = {1}", + arg0: x, + arg1: groupTexts[x])); + } + + /* + Recombination Rules. + When recombining the translated three-digit groups, each group except the + last is followed by a large number name and a comma, unless the group is + blank and therefore not included at all. One exception is when the final + group does not include any hundreds and there is more than one non-blank + group. In this case, the final comma is replaced with 'and'. eg. + 'one billion, one million and twelve'. + */ + + // Recombine the three-digit groups + string combined = groupTexts[0]; + bool appendAnd; + + // Determine whether an 'and' is needed + appendAnd = (digitGroups[0] > 0) && (digitGroups[0] < 100); + + // Process the remaining groups in turn, smallest to largest + for (int i = 1; i < groups; i++) + { + // Only add non-zero items + if (digitGroups[i] != 0) + { + // Build the string to add as a prefix + string prefix = groupTexts[i] + " " + scaleNumbers[i]; + + if (combined.Length != 0) + { + prefix += appendAnd ? " and " : ", "; + } + + // Opportunity to add 'and' is ended + appendAnd = false; + + // Add the three-digit group to the combined string + combined = prefix + combined; + } + } + + // Converts a three-digit group into English words + string ThreeDigitGroupToWords(int threeDigits) + { + // Initialise the return text + string groupText = ""; + + // Determine the hundreds and the remainder + int hundreds = threeDigits / 100; + int tensUnits = threeDigits % 100; + + /* + Hundreds Rules. + If the hundreds portion of a three-digit group is not zero, the number of + hundreds is added as a word. If the three-digit group is exactly divisible + by one hundred, the text 'hundred' is appended. If not, the text + "hundred and" is appended. eg. 'two hundred' or 'one hundred and twelve' + */ + + if (hundreds != 0) + { + groupText += smallNumbers[hundreds] + " hundred"; + + if (tensUnits != 0) + { + groupText += " and "; + } + } + + // Determine the tens and units + int tens = tensUnits / 10; + int units = tensUnits % 10; + + /* Tens Rules. + If the tens section of a three-digit group is two or higher, the appropriate + '-ty' word (twenty, thirty, etc.) is added to the text and followed by the + name of the third digit (unless the third digit is a zero, which is ignored). + If the tens and the units are both zero, no text is added. For any other value, + the name of the one or two-digit number is added as a special case. + */ + + if (tens >= 2) + { + groupText += NumbersToWords.tens[tens]; + if (units != 0) + { + groupText += " " + smallNumbers[units]; + } + } + else if (tensUnits != 0) + groupText += smallNumbers[tensUnits]; + + return groupText; + } + + /* Negative Rule. + Negative numbers are always preceded by the text 'negative'. + */ + if (number < 0) + { + combined = "negative " + combined; + } + + return combined; + } + } +} diff --git a/vs4win/Chapter08/Ch08Ex03NumbersAsWordsTests/Ch08Ex03NumbersAsWordsTests.csproj b/vs4win/Chapter08/Ch08Ex03NumbersAsWordsTests/Ch08Ex03NumbersAsWordsTests.csproj new file mode 100644 index 0000000..de9c08a --- /dev/null +++ b/vs4win/Chapter08/Ch08Ex03NumbersAsWordsTests/Ch08Ex03NumbersAsWordsTests.csproj @@ -0,0 +1,28 @@ + + + + net7.0 + enable + enable + + false + + + + + + + runtime; build; native; contentfiles; analyzers; buildtransitive + all + + + runtime; build; native; contentfiles; analyzers; buildtransitive + all + + + + + + + + diff --git a/vs4win/Chapter08/Ch08Ex03NumbersAsWordsTests/NumbersToWordsUnitTests.cs b/vs4win/Chapter08/Ch08Ex03NumbersAsWordsTests/NumbersToWordsUnitTests.cs new file mode 100644 index 0000000..9a386df --- /dev/null +++ b/vs4win/Chapter08/Ch08Ex03NumbersAsWordsTests/NumbersToWordsUnitTests.cs @@ -0,0 +1,64 @@ +using Packt.Shared; // ToWords extension method +using System.Numerics; // BigInteger + +namespace Ch08Ex03NumbersAsWordsTests +{ + public class NumbersToWordsUnitTests + { + [Fact] + public void Test_Int32_0() + { + // arrange + int number = 0; + string expected = "zero"; + + // act + string actual = number.ToWords(); + + // assert + Assert.Equal(expected, actual); + } + + [Fact] + public void Test_Int32_1234() + { + // arrange + int number = 1234; + string expected = "one thousand, two hundred and thirty four"; + + // act + string actual = number.ToWords(); + + // assert + Assert.Equal(expected, actual); + } + + [Fact] + public void Test_BigInteger_18456002032011000007() + { + // arrange + BigInteger number = BigInteger.Parse("18456002032011000007"); + string expected = "eighteen quintillion, four hundred and fifty six quadrillion, two trillion, thirty two billion, eleven million and seven"; + + // act + string actual = number.ToWords(); + + // assert + Assert.Equal(expected, actual); + } + + [Fact] + public void Test_Int32_minus_13() + { + // arrange + int number = -13; + string expected = "negative thirteen"; + + // act + string actual = number.ToWords(); + + // assert + Assert.Equal(expected, actual); + } + } +} \ No newline at end of file diff --git a/vs4win/Chapter08/Ch08Ex03NumbersAsWordsTests/Usings.cs b/vs4win/Chapter08/Ch08Ex03NumbersAsWordsTests/Usings.cs new file mode 100644 index 0000000..8c927eb --- /dev/null +++ b/vs4win/Chapter08/Ch08Ex03NumbersAsWordsTests/Usings.cs @@ -0,0 +1 @@ +global using Xunit; \ No newline at end of file diff --git a/vs4win/Chapter08/Chapter08.sln b/vs4win/Chapter08/Chapter08.sln new file mode 100644 index 0000000..efa8191 --- /dev/null +++ b/vs4win/Chapter08/Chapter08.sln @@ -0,0 +1,79 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio Version 17 +VisualStudioVersion = 17.2.32210.308 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "WorkingWithNumbers", "WorkingWithNumbers\WorkingWithNumbers.csproj", "{EF42A2EE-FB4A-4BE4-A331-2B3D6CC1A1C5}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "WorkingWithText", "WorkingWithText\WorkingWithText.csproj", "{BD0568D2-A0C9-4FB5-AD3A-A685E794A29E}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "WorkingWithRegularExpressions", "WorkingWithRegularExpressions\WorkingWithRegularExpressions.csproj", "{221EF67C-EB18-417F-811F-2836BBC4B9ED}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "WorkingWithCollections", "WorkingWithCollections\WorkingWithCollections.csproj", "{678CA866-8E00-48EB-A8F5-1D587BFB311A}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "WorkingWithRanges", "WorkingWithRanges\WorkingWithRanges.csproj", "{D93AB524-D62D-422B-895A-B1F0ACD3E137}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "WorkingWithNetworkResources", "WorkingWithNetworkResources\WorkingWithNetworkResources.csproj", "{5A9BBCB8-F680-45DA-BBAD-DC20F3FBEE4E}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Ch08Ex02RegularExpressions", "Ch08Ex02RegularExpressions\Ch08Ex02RegularExpressions.csproj", "{BDCF7297-FA66-437C-A428-AF1B32C520DD}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Ch08Ex03NumbersAsWordsLib", "Ch08Ex03NumbersAsWordsLib\Ch08Ex03NumbersAsWordsLib.csproj", "{42F893F1-B861-4B4B-A14B-4F278E11F87F}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Ch08Ex03NumbersAsWordsApp", "Ch08Ex03NumbersAsWordsApp\Ch08Ex03NumbersAsWordsApp.csproj", "{25385931-197B-4713-82B9-74764DBD76A3}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Ch08Ex03NumbersAsWordsTests", "Ch08Ex03NumbersAsWordsTests\Ch08Ex03NumbersAsWordsTests.csproj", "{793E6479-9AE4-4802-B5BF-BC4CED5E72F0}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {EF42A2EE-FB4A-4BE4-A331-2B3D6CC1A1C5}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {EF42A2EE-FB4A-4BE4-A331-2B3D6CC1A1C5}.Debug|Any CPU.Build.0 = Debug|Any CPU + {EF42A2EE-FB4A-4BE4-A331-2B3D6CC1A1C5}.Release|Any CPU.ActiveCfg = Release|Any CPU + {EF42A2EE-FB4A-4BE4-A331-2B3D6CC1A1C5}.Release|Any CPU.Build.0 = Release|Any CPU + {BD0568D2-A0C9-4FB5-AD3A-A685E794A29E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {BD0568D2-A0C9-4FB5-AD3A-A685E794A29E}.Debug|Any CPU.Build.0 = Debug|Any CPU + {BD0568D2-A0C9-4FB5-AD3A-A685E794A29E}.Release|Any CPU.ActiveCfg = Release|Any CPU + {BD0568D2-A0C9-4FB5-AD3A-A685E794A29E}.Release|Any CPU.Build.0 = Release|Any CPU + {221EF67C-EB18-417F-811F-2836BBC4B9ED}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {221EF67C-EB18-417F-811F-2836BBC4B9ED}.Debug|Any CPU.Build.0 = Debug|Any CPU + {221EF67C-EB18-417F-811F-2836BBC4B9ED}.Release|Any CPU.ActiveCfg = Release|Any CPU + {221EF67C-EB18-417F-811F-2836BBC4B9ED}.Release|Any CPU.Build.0 = Release|Any CPU + {678CA866-8E00-48EB-A8F5-1D587BFB311A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {678CA866-8E00-48EB-A8F5-1D587BFB311A}.Debug|Any CPU.Build.0 = Debug|Any CPU + {678CA866-8E00-48EB-A8F5-1D587BFB311A}.Release|Any CPU.ActiveCfg = Release|Any CPU + {678CA866-8E00-48EB-A8F5-1D587BFB311A}.Release|Any CPU.Build.0 = Release|Any CPU + {D93AB524-D62D-422B-895A-B1F0ACD3E137}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {D93AB524-D62D-422B-895A-B1F0ACD3E137}.Debug|Any CPU.Build.0 = Debug|Any CPU + {D93AB524-D62D-422B-895A-B1F0ACD3E137}.Release|Any CPU.ActiveCfg = Release|Any CPU + {D93AB524-D62D-422B-895A-B1F0ACD3E137}.Release|Any CPU.Build.0 = Release|Any CPU + {5A9BBCB8-F680-45DA-BBAD-DC20F3FBEE4E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {5A9BBCB8-F680-45DA-BBAD-DC20F3FBEE4E}.Debug|Any CPU.Build.0 = Debug|Any CPU + {5A9BBCB8-F680-45DA-BBAD-DC20F3FBEE4E}.Release|Any CPU.ActiveCfg = Release|Any CPU + {5A9BBCB8-F680-45DA-BBAD-DC20F3FBEE4E}.Release|Any CPU.Build.0 = Release|Any CPU + {BDCF7297-FA66-437C-A428-AF1B32C520DD}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {BDCF7297-FA66-437C-A428-AF1B32C520DD}.Debug|Any CPU.Build.0 = Debug|Any CPU + {BDCF7297-FA66-437C-A428-AF1B32C520DD}.Release|Any CPU.ActiveCfg = Release|Any CPU + {BDCF7297-FA66-437C-A428-AF1B32C520DD}.Release|Any CPU.Build.0 = Release|Any CPU + {42F893F1-B861-4B4B-A14B-4F278E11F87F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {42F893F1-B861-4B4B-A14B-4F278E11F87F}.Debug|Any CPU.Build.0 = Debug|Any CPU + {42F893F1-B861-4B4B-A14B-4F278E11F87F}.Release|Any CPU.ActiveCfg = Release|Any CPU + {42F893F1-B861-4B4B-A14B-4F278E11F87F}.Release|Any CPU.Build.0 = Release|Any CPU + {25385931-197B-4713-82B9-74764DBD76A3}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {25385931-197B-4713-82B9-74764DBD76A3}.Debug|Any CPU.Build.0 = Debug|Any CPU + {25385931-197B-4713-82B9-74764DBD76A3}.Release|Any CPU.ActiveCfg = Release|Any CPU + {25385931-197B-4713-82B9-74764DBD76A3}.Release|Any CPU.Build.0 = Release|Any CPU + {793E6479-9AE4-4802-B5BF-BC4CED5E72F0}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {793E6479-9AE4-4802-B5BF-BC4CED5E72F0}.Debug|Any CPU.Build.0 = Debug|Any CPU + {793E6479-9AE4-4802-B5BF-BC4CED5E72F0}.Release|Any CPU.ActiveCfg = Release|Any CPU + {793E6479-9AE4-4802-B5BF-BC4CED5E72F0}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {BD4295A9-D6CF-470E-8220-CDA9A1FEA2C6} + EndGlobalSection +EndGlobal diff --git a/vs4win/Chapter08/WorkingWithCollections/Program.Helpers.cs b/vs4win/Chapter08/WorkingWithCollections/Program.Helpers.cs new file mode 100644 index 0000000..a31041c --- /dev/null +++ b/vs4win/Chapter08/WorkingWithCollections/Program.Helpers.cs @@ -0,0 +1,21 @@ +partial class Program +{ + static void Output(string title, IEnumerable collection) + { + WriteLine(title); + foreach (string item in collection) + { + WriteLine($" {item}"); + } + } + + static void OutputPQ(string title, + IEnumerable<(TElement Element, TPriority Priority)> collection) + { + WriteLine(title); + foreach ((TElement, TPriority) item in collection) + { + WriteLine($" {item.Item1}: {item.Item2}"); + } + } +} diff --git a/vs4win/Chapter08/WorkingWithCollections/Program.cs b/vs4win/Chapter08/WorkingWithCollections/Program.cs new file mode 100644 index 0000000..599e08e --- /dev/null +++ b/vs4win/Chapter08/WorkingWithCollections/Program.cs @@ -0,0 +1,129 @@ +using System.Collections.Immutable; + +// Working with lists + +// Simple syntax for creating a list and adding three items +List cities = new(); +cities.Add("London"); +cities.Add("Paris"); +cities.Add("Milan"); + +/* Alternative syntax that is converted by the compiler into + the three Add method calls above +List cities = new() + { "London", "Paris", "Milan" }; */ + +/* Alternative syntax that passes an + array of string values to AddRange method +List cities = new(); +cities.AddRange(new[] { "London", "Paris", "Milan" }); */ + +Output("Initial list", cities); +WriteLine($"The first city is {cities[0]}."); +WriteLine($"The last city is {cities[cities.Count - 1]}."); + +cities.Insert(0, "Sydney"); +Output("After inserting Sydney at index 0", cities); + +cities.RemoveAt(1); +cities.Remove("Milan"); +Output("After removing two cities", cities); + +// Working with dictionaries + +Dictionary keywords = new(); + +// add using named parameters +keywords.Add(key: "int", value: "32-bit integer data type"); + +// add using positional parameters +keywords.Add("long", "64-bit integer data type"); +keywords.Add("float", "Single precision floating point number"); + +/* Alternative syntax; compiler converts this to calls to Add method +Dictionary keywords = new() +{ + { "int", "32-bit integer data type" }, + { "long", "64-bit integer data type" }, + { "float", "Single precision floating point number" }, +}; */ + +/* Alternative syntax; compiler converts this to calls to Add method +Dictionary keywords = new() +{ + ["int"] = "32-bit integer data type", + ["long"] = "64-bit integer data type", + ["float"] = "Single precision floating point number", // last comma is optional +}; */ + +Output("Dictionary keys:", keywords.Keys); +Output("Dictionary values:", keywords.Values); + +WriteLine("Keywords and their definitions"); +foreach (KeyValuePair item in keywords) +{ + WriteLine($" {item.Key}: {item.Value}"); +} + +// lookup a value using a key +string key = "long"; +WriteLine($"The definition of {key} is {keywords[key]}"); + +// Working with queues + +Queue coffee = new(); + +coffee.Enqueue("Damir"); // front of queue +coffee.Enqueue("Andrea"); +coffee.Enqueue("Ronald"); +coffee.Enqueue("Amin"); +coffee.Enqueue("Irina"); // back of queue + +Output("Initial queue from front to back", coffee); + +// server handles next person in queue +string served = coffee.Dequeue(); +WriteLine($"Served: {served}."); + +// server handles next person in queue +served = coffee.Dequeue(); +WriteLine($"Served: {served}."); +Output("Current queue from front to back", coffee); + +WriteLine($"{coffee.Peek()} is next in line."); +Output("Current queue from front to back", coffee); + +// Working with priority queues + +PriorityQueue vaccine = new(); + +// add some people +// 1 = high priority people in their 70s or poor health +// 2 = medium priority e.g. middle aged +// 3 = low priority e.g. teens and twenties + +vaccine.Enqueue("Pamela", 1); // my mum (70s) +vaccine.Enqueue("Rebecca", 3); // my niece (teens) +vaccine.Enqueue("Juliet", 2); // my sister (40s) +vaccine.Enqueue("Ian", 1); // my dad (70s) + +OutputPQ("Current queue for vaccination:", vaccine.UnorderedItems); + +WriteLine($"{vaccine.Dequeue()} has been vaccinated."); +WriteLine($"{vaccine.Dequeue()} has been vaccinated."); +OutputPQ("Current queue for vaccination:", vaccine.UnorderedItems); + +WriteLine($"{vaccine.Dequeue()} has been vaccinated."); + +WriteLine("Adding Mark to queue with priority 2"); +vaccine.Enqueue("Mark", 2); // me (50s) + +WriteLine($"{vaccine.Peek()} will be next to be vaccinated."); +OutputPQ("Current queue for vaccination:", vaccine.UnorderedItems); + +// Working with immutable collections + +ImmutableList immutableCities = cities.ToImmutableList(); +ImmutableList newList = immutableCities.Add("Rio"); +Output("Immutable list of cities:", immutableCities); +Output("New list of cities:", newList); diff --git a/vs4win/Chapter08/WorkingWithCollections/WorkingWithCollections.csproj b/vs4win/Chapter08/WorkingWithCollections/WorkingWithCollections.csproj new file mode 100644 index 0000000..cd63b28 --- /dev/null +++ b/vs4win/Chapter08/WorkingWithCollections/WorkingWithCollections.csproj @@ -0,0 +1,14 @@ + + + + Exe + net7.0 + enable + enable + + + + + + + diff --git a/vs4win/Chapter08/WorkingWithNetworkResources/Program.cs b/vs4win/Chapter08/WorkingWithNetworkResources/Program.cs new file mode 100644 index 0000000..e614734 --- /dev/null +++ b/vs4win/Chapter08/WorkingWithNetworkResources/Program.cs @@ -0,0 +1,50 @@ +using System.Net; // IPHostEntry, Dns, IPAddress +using System.Net.NetworkInformation; // Ping, PingReply, IPStatus + +// Working with URIs, DNS, and IP addresses + +Write("Enter a valid web address (or press Enter): "); +string? url = ReadLine(); + +if (string.IsNullOrWhiteSpace(url)) // if they enter nothing... +{ + // ... set a default URL + url = "https://stackoverflow.com/search?q=securestring"; +} + +Uri uri = new(url); +WriteLine($"URL: {url}"); +WriteLine($"Scheme: {uri.Scheme}"); +WriteLine($"Port: {uri.Port}"); +WriteLine($"Host: {uri.Host}"); +WriteLine($"Path: {uri.AbsolutePath}"); +WriteLine($"Query: {uri.Query}"); + +IPHostEntry entry = Dns.GetHostEntry(uri.Host); +WriteLine($"{entry.HostName} has the following IP addresses:"); +foreach (IPAddress address in entry.AddressList) +{ + WriteLine($" {address} ({address.AddressFamily})"); +} + +// Pinging a server + +try +{ + Ping ping = new(); + + WriteLine("Pinging server. Please wait..."); + PingReply reply = ping.Send(uri.Host); + WriteLine($"{uri.Host} was pinged and replied: {reply.Status}."); + + if (reply.Status == IPStatus.Success) + { + WriteLine("Reply from {0} took {1:N0}ms", + arg0: reply.Address, + arg1: reply.RoundtripTime); + } +} +catch (Exception ex) +{ + WriteLine($"{ex.GetType().ToString()} says {ex.Message}"); +} diff --git a/vs4win/Chapter08/WorkingWithNetworkResources/WorkingWithNetworkResources.csproj b/vs4win/Chapter08/WorkingWithNetworkResources/WorkingWithNetworkResources.csproj new file mode 100644 index 0000000..91807b2 --- /dev/null +++ b/vs4win/Chapter08/WorkingWithNetworkResources/WorkingWithNetworkResources.csproj @@ -0,0 +1,14 @@ + + + + Exe + net7.0 + enable + enable + + + + + + + diff --git a/vs4win/Chapter08/WorkingWithNumbers/Program.cs b/vs4win/Chapter08/WorkingWithNumbers/Program.cs new file mode 100644 index 0000000..7985a1e --- /dev/null +++ b/vs4win/Chapter08/WorkingWithNumbers/Program.cs @@ -0,0 +1,27 @@ +using System.Numerics; + +WriteLine("Working with large integers:"); +WriteLine("-----------------------------------"); + +ulong big = ulong.MaxValue; +WriteLine($"{big,40:N0}"); + +BigInteger bigger = + BigInteger.Parse("123456789012345678901234567890"); +WriteLine($"{bigger,40:N0}"); + +WriteLine("Working with complex numbers:"); + +Complex c1 = new(real: 4, imaginary: 2); +Complex c2 = new(real: 3, imaginary: 7); +Complex c3 = c1 + c2; + +// output using default ToString implementation +WriteLine($"{c1} added to {c2} is {c3}"); + +// output using custom format +WriteLine("{0} + {1}i added to {2} + {3}i is {4} + {5}i", + c1.Real, c1.Imaginary, + c2.Real, c2.Imaginary, + c3.Real, c3.Imaginary); + diff --git a/vs4win/Chapter08/WorkingWithNumbers/WorkingWithNumbers.csproj b/vs4win/Chapter08/WorkingWithNumbers/WorkingWithNumbers.csproj new file mode 100644 index 0000000..cd63b28 --- /dev/null +++ b/vs4win/Chapter08/WorkingWithNumbers/WorkingWithNumbers.csproj @@ -0,0 +1,14 @@ + + + + Exe + net7.0 + enable + enable + + + + + + + diff --git a/vs4win/Chapter08/WorkingWithRanges/Program.cs b/vs4win/Chapter08/WorkingWithRanges/Program.cs new file mode 100644 index 0000000..584461f --- /dev/null +++ b/vs4win/Chapter08/WorkingWithRanges/Program.cs @@ -0,0 +1,28 @@ +string name = "Samantha Jones"; + +// getting the lengths of the first and last names + +int lengthOfFirst = name.IndexOf(' '); +int lengthOfLast = name.Length - lengthOfFirst - 1; + +// Using Substring + +string firstName = name.Substring( + startIndex: 0, + length: lengthOfFirst); + +string lastName = name.Substring( + startIndex: name.Length - lengthOfLast, + length: lengthOfLast); + +WriteLine($"First name: {firstName}, Last name: {lastName}"); + +// Using spans + +ReadOnlySpan nameAsSpan = name.AsSpan(); +ReadOnlySpan firstNameSpan = nameAsSpan[0..lengthOfFirst]; +ReadOnlySpan lastNameSpan = nameAsSpan[^lengthOfLast..^0]; + +WriteLine("First name: {0}, Last name: {1}", + arg0: firstNameSpan.ToString(), + arg1: lastNameSpan.ToString()); diff --git a/vs4win/Chapter08/WorkingWithRanges/WorkingWithRanges.csproj b/vs4win/Chapter08/WorkingWithRanges/WorkingWithRanges.csproj new file mode 100644 index 0000000..cd63b28 --- /dev/null +++ b/vs4win/Chapter08/WorkingWithRanges/WorkingWithRanges.csproj @@ -0,0 +1,14 @@ + + + + Exe + net7.0 + enable + enable + + + + + + + diff --git a/vs4win/Chapter08/WorkingWithRegularExpressions/Program.cs b/vs4win/Chapter08/WorkingWithRegularExpressions/Program.cs new file mode 100644 index 0000000..434bbd8 --- /dev/null +++ b/vs4win/Chapter08/WorkingWithRegularExpressions/Program.cs @@ -0,0 +1,38 @@ +using System.Text.RegularExpressions; // Regex + +Write("Enter your age: "); +string input = ReadLine()!; // null-forgiving + +Regex ageChecker = new(@"^\d+$"); + +if (ageChecker.IsMatch(input)) +{ + WriteLine("Thank you!"); +} +else +{ + WriteLine($"This is not a valid age: {input}"); +} + +string films = "\"Monsters, Inc.\",\"I, Tonya\",\"Lock, Stock and Two Smoking Barrels\""; + +WriteLine($"Films to split: {films}"); + +string[] filmsDumb = films.Split(','); + +WriteLine("Splitting with string.Split method:"); +foreach (string film in filmsDumb) +{ + WriteLine(film); +} + +Regex csv = new( + "(?:^|,)(?=[^\"]|(\")?)\"?((?(1)[^\"]*|[^,\"]*))\"?(?=,|$)"); + +MatchCollection filmsSmart = csv.Matches(films); + +WriteLine("Splitting with regular expression:"); +foreach (Match film in filmsSmart) +{ + WriteLine(film.Groups[2].Value); +} diff --git a/vs4win/Chapter08/WorkingWithRegularExpressions/WorkingWithRegularExpressions.csproj b/vs4win/Chapter08/WorkingWithRegularExpressions/WorkingWithRegularExpressions.csproj new file mode 100644 index 0000000..cd63b28 --- /dev/null +++ b/vs4win/Chapter08/WorkingWithRegularExpressions/WorkingWithRegularExpressions.csproj @@ -0,0 +1,14 @@ + + + + Exe + net7.0 + enable + enable + + + + + + + diff --git a/vs4win/Chapter08/WorkingWithText/Program.cs b/vs4win/Chapter08/WorkingWithText/Program.cs new file mode 100644 index 0000000..139e2ed --- /dev/null +++ b/vs4win/Chapter08/WorkingWithText/Program.cs @@ -0,0 +1,45 @@ +string city = "London"; +WriteLine($"{city} is {city.Length} characters long."); + +WriteLine($"First char is {city[0]} and third is {city[2]}."); + +string cities = "Paris,Tehran,Chennai,Sydney,New York,Medellín"; + +string[] citiesArray = cities.Split(','); + +WriteLine($"There are {citiesArray.Length} items in the array."); + +foreach (string item in citiesArray) +{ + WriteLine(item); +} + +string fullName = "Alan Jones"; + +int indexOfTheSpace = fullName.IndexOf(' '); + +string firstName = fullName.Substring( + startIndex: 0, length: indexOfTheSpace); + +string lastName = fullName.Substring( + startIndex: indexOfTheSpace + 1); + +WriteLine($"Original: {fullName}"); +WriteLine($"Swapped: {lastName}, {firstName}"); + +string company = "Microsoft"; +bool startsWithM = company.StartsWith("M"); +bool containsN = company.Contains("N"); +WriteLine($"Text: {company}"); +WriteLine($"Starts with M: {startsWithM}, contains an N: {containsN}"); + +string recombined = string.Join(" => ", citiesArray); +WriteLine(recombined); + +string fruit = "Apples"; +decimal price = 0.39M; +DateTime when = DateTime.Today; + +WriteLine($"Interpolated: {fruit} cost {price:C} on {when:dddd}."); +WriteLine(string.Format("string.Format: {0} cost {1:C} on {2:dddd}.", + arg0: fruit, arg1: price, arg2: when)); diff --git a/vs4win/Chapter08/WorkingWithText/WorkingWithText.csproj b/vs4win/Chapter08/WorkingWithText/WorkingWithText.csproj new file mode 100644 index 0000000..cd63b28 --- /dev/null +++ b/vs4win/Chapter08/WorkingWithText/WorkingWithText.csproj @@ -0,0 +1,14 @@ + + + + Exe + net7.0 + enable + enable + + + + + + + diff --git a/vscode/Chapter08/Ch08Ex02RegularExpressions/Ch08Ex02RegularExpressions.csproj b/vscode/Chapter08/Ch08Ex02RegularExpressions/Ch08Ex02RegularExpressions.csproj new file mode 100644 index 0000000..6431db6 --- /dev/null +++ b/vscode/Chapter08/Ch08Ex02RegularExpressions/Ch08Ex02RegularExpressions.csproj @@ -0,0 +1,14 @@ + + + + Exe + net7.0 + enable + enable + + + + + + + diff --git a/vscode/Chapter08/Ch08Ex02RegularExpressions/Program.cs b/vscode/Chapter08/Ch08Ex02RegularExpressions/Program.cs new file mode 100644 index 0000000..118f186 --- /dev/null +++ b/vscode/Chapter08/Ch08Ex02RegularExpressions/Program.cs @@ -0,0 +1,24 @@ +using System.Text.RegularExpressions; + +WriteLine("The default regular expression checks for at least one digit."); + +do +{ + Write("Enter a regular expression (or press ENTER to use the default): "); + string? regexp = ReadLine(); + + if (string.IsNullOrWhiteSpace(regexp)) + { + regexp = @"^\d+$"; + } + + Write("Enter some input: "); + string input = ReadLine()!; // will never be null + + Regex r = new(regexp); + + WriteLine($"{input} matches {regexp}: {r.IsMatch(input)}"); + + WriteLine("Press ESC to end or any key to try again."); +} +while (ReadKey(intercept: true).Key != ConsoleKey.Escape); \ No newline at end of file diff --git a/vscode/Chapter08/Ch08Ex03NumbersAsWordsApp/Ch08Ex03NumbersAsWordsApp.csproj b/vscode/Chapter08/Ch08Ex03NumbersAsWordsApp/Ch08Ex03NumbersAsWordsApp.csproj new file mode 100644 index 0000000..c91be9d --- /dev/null +++ b/vscode/Chapter08/Ch08Ex03NumbersAsWordsApp/Ch08Ex03NumbersAsWordsApp.csproj @@ -0,0 +1,18 @@ + + + + Exe + net7.0 + enable + enable + + + + + + + + + + + diff --git a/vscode/Chapter08/Ch08Ex03NumbersAsWordsApp/Program.cs b/vscode/Chapter08/Ch08Ex03NumbersAsWordsApp/Program.cs new file mode 100644 index 0000000..7caac9e --- /dev/null +++ b/vscode/Chapter08/Ch08Ex03NumbersAsWordsApp/Program.cs @@ -0,0 +1,16 @@ +using Packt.Shared; // ToWords extension method +using System.Numerics; // BigInteger + +Write("Enter a number up to twenty one digits long: "); +string? input = ReadLine(); +if (input is null) return; + +if (input.Length > 21) +{ + WriteLine("I cannot handle more than twenty one digits!"); + return; +} + +BigInteger number = BigInteger.Parse(input); + +WriteLine($"{number:N0} in words is {number.ToWords()}."); \ No newline at end of file diff --git a/vscode/Chapter08/Ch08Ex03NumbersAsWordsLib/Ch08Ex03NumbersAsWordsLib.csproj b/vscode/Chapter08/Ch08Ex03NumbersAsWordsLib/Ch08Ex03NumbersAsWordsLib.csproj new file mode 100644 index 0000000..9f5c4f4 --- /dev/null +++ b/vscode/Chapter08/Ch08Ex03NumbersAsWordsLib/Ch08Ex03NumbersAsWordsLib.csproj @@ -0,0 +1,7 @@ + + + + netstandard2.0 + + + diff --git a/vscode/Chapter08/Ch08Ex03NumbersAsWordsLib/NumbersToWords.cs b/vscode/Chapter08/Ch08Ex03NumbersAsWordsLib/NumbersToWords.cs new file mode 100644 index 0000000..5ea0834 --- /dev/null +++ b/vscode/Chapter08/Ch08Ex03NumbersAsWordsLib/NumbersToWords.cs @@ -0,0 +1,220 @@ +using System.Diagnostics; // Trace +using System.IO; // File +using System.Numerics; // BigInteger + +namespace Packt.Shared +{ + public static class NumbersToWords + { + // 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" + }; + + private static int groups = 7; // i.e. up to quintillion + + public static string ToWords(this int number) + { + return ToWords((BigInteger)number); + } + + public static string ToWords(this long number) + { + return ToWords((BigInteger)number); + } + + public static string ToWords(this BigInteger number) + { + /* + Convert A Number into Words + by Richard Carr, published at http://www.blackwasp.co.uk/numbertowords.aspx + */ + + /* + Zero Rule. + If the value is 0 then the number in words is 'zero' and no other rules apply. + */ + if (number == 0) + { + return "zero"; + } + + /* + Three Digit Rule. + The integer value is split into groups of three digits starting from the + right-hand side. Each set of three digits is then processed individually + as a number of hundreds, tens and units. Once converted to text, the + three-digit groups are recombined with the addition of the relevant scale + number (thousand, million, billion). + */ + + // Array to hold the specified number of three-digit groups + 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; + } + + // write to a text file in the project folder + Trace.Listeners.Add(new TextWriterTraceListener( + File.AppendText("log.txt"))); + + // text writer is buffered, so this option calls + // Flush() on all listeners after writing + Trace.AutoFlush = true; + + // log array of group numbers + for (int x = 0; x < digitGroups.Length; x++) + { + Trace.WriteLine(string.Format( + format: "digitGroups[{0}] = {1}", + arg0: x, + arg1: digitGroups[x])); + } + + // Convert each three-digit group to words + string[] groupTexts = new string[groups]; + + for (int i = 0; i < groups; i++) + { + // call a local function (see below) + groupTexts[i] = ThreeDigitGroupToWords(digitGroups[i]); + } + + // log array of group texts + for (int x = 0; x < groupTexts.Length; x++) + { + Trace.WriteLine(string.Format( + format: "groupTexts[{0}] = {1}", + arg0: x, + arg1: groupTexts[x])); + } + + /* + Recombination Rules. + When recombining the translated three-digit groups, each group except the + last is followed by a large number name and a comma, unless the group is + blank and therefore not included at all. One exception is when the final + group does not include any hundreds and there is more than one non-blank + group. In this case, the final comma is replaced with 'and'. eg. + 'one billion, one million and twelve'. + */ + + // Recombine the three-digit groups + string combined = groupTexts[0]; + bool appendAnd; + + // Determine whether an 'and' is needed + appendAnd = (digitGroups[0] > 0) && (digitGroups[0] < 100); + + // Process the remaining groups in turn, smallest to largest + for (int i = 1; i < groups; i++) + { + // Only add non-zero items + if (digitGroups[i] != 0) + { + // Build the string to add as a prefix + string prefix = groupTexts[i] + " " + scaleNumbers[i]; + + if (combined.Length != 0) + { + prefix += appendAnd ? " and " : ", "; + } + + // Opportunity to add 'and' is ended + appendAnd = false; + + // Add the three-digit group to the combined string + combined = prefix + combined; + } + } + + // Converts a three-digit group into English words + string ThreeDigitGroupToWords(int threeDigits) + { + // Initialise the return text + string groupText = ""; + + // Determine the hundreds and the remainder + int hundreds = threeDigits / 100; + int tensUnits = threeDigits % 100; + + /* + Hundreds Rules. + If the hundreds portion of a three-digit group is not zero, the number of + hundreds is added as a word. If the three-digit group is exactly divisible + by one hundred, the text 'hundred' is appended. If not, the text + "hundred and" is appended. eg. 'two hundred' or 'one hundred and twelve' + */ + + if (hundreds != 0) + { + groupText += smallNumbers[hundreds] + " hundred"; + + if (tensUnits != 0) + { + groupText += " and "; + } + } + + // Determine the tens and units + int tens = tensUnits / 10; + int units = tensUnits % 10; + + /* Tens Rules. + If the tens section of a three-digit group is two or higher, the appropriate + '-ty' word (twenty, thirty, etc.) is added to the text and followed by the + name of the third digit (unless the third digit is a zero, which is ignored). + If the tens and the units are both zero, no text is added. For any other value, + the name of the one or two-digit number is added as a special case. + */ + + if (tens >= 2) + { + groupText += NumbersToWords.tens[tens]; + if (units != 0) + { + groupText += " " + smallNumbers[units]; + } + } + else if (tensUnits != 0) + groupText += smallNumbers[tensUnits]; + + return groupText; + } + + /* Negative Rule. + Negative numbers are always preceded by the text 'negative'. + */ + if (number < 0) + { + combined = "negative " + combined; + } + + return combined; + } + } +} diff --git a/vscode/Chapter08/Ch08Ex03NumbersAsWordsTests/Ch08Ex03NumbersAsWordsTests.csproj b/vscode/Chapter08/Ch08Ex03NumbersAsWordsTests/Ch08Ex03NumbersAsWordsTests.csproj new file mode 100644 index 0000000..de9c08a --- /dev/null +++ b/vscode/Chapter08/Ch08Ex03NumbersAsWordsTests/Ch08Ex03NumbersAsWordsTests.csproj @@ -0,0 +1,28 @@ + + + + net7.0 + enable + enable + + false + + + + + + + runtime; build; native; contentfiles; analyzers; buildtransitive + all + + + runtime; build; native; contentfiles; analyzers; buildtransitive + all + + + + + + + + diff --git a/vscode/Chapter08/Ch08Ex03NumbersAsWordsTests/NumbersToWordsUnitTests.cs b/vscode/Chapter08/Ch08Ex03NumbersAsWordsTests/NumbersToWordsUnitTests.cs new file mode 100644 index 0000000..9a386df --- /dev/null +++ b/vscode/Chapter08/Ch08Ex03NumbersAsWordsTests/NumbersToWordsUnitTests.cs @@ -0,0 +1,64 @@ +using Packt.Shared; // ToWords extension method +using System.Numerics; // BigInteger + +namespace Ch08Ex03NumbersAsWordsTests +{ + public class NumbersToWordsUnitTests + { + [Fact] + public void Test_Int32_0() + { + // arrange + int number = 0; + string expected = "zero"; + + // act + string actual = number.ToWords(); + + // assert + Assert.Equal(expected, actual); + } + + [Fact] + public void Test_Int32_1234() + { + // arrange + int number = 1234; + string expected = "one thousand, two hundred and thirty four"; + + // act + string actual = number.ToWords(); + + // assert + Assert.Equal(expected, actual); + } + + [Fact] + public void Test_BigInteger_18456002032011000007() + { + // arrange + BigInteger number = BigInteger.Parse("18456002032011000007"); + string expected = "eighteen quintillion, four hundred and fifty six quadrillion, two trillion, thirty two billion, eleven million and seven"; + + // act + string actual = number.ToWords(); + + // assert + Assert.Equal(expected, actual); + } + + [Fact] + public void Test_Int32_minus_13() + { + // arrange + int number = -13; + string expected = "negative thirteen"; + + // act + string actual = number.ToWords(); + + // assert + Assert.Equal(expected, actual); + } + } +} \ No newline at end of file diff --git a/vscode/Chapter08/Ch08Ex03NumbersAsWordsTests/Usings.cs b/vscode/Chapter08/Ch08Ex03NumbersAsWordsTests/Usings.cs new file mode 100644 index 0000000..8c927eb --- /dev/null +++ b/vscode/Chapter08/Ch08Ex03NumbersAsWordsTests/Usings.cs @@ -0,0 +1 @@ +global using Xunit; \ No newline at end of file diff --git a/vscode/Chapter08/Chapter08.code-workspace b/vscode/Chapter08/Chapter08.code-workspace new file mode 100644 index 0000000..9156337 --- /dev/null +++ b/vscode/Chapter08/Chapter08.code-workspace @@ -0,0 +1,34 @@ +{ + "folders": [ + { + "path": "WorkingWithNumbers" + }, + { + "path": "WorkingWithText" + }, + { + "path": "WorkingWithRegularExpressions" + }, + { + "path": "WorkingWithCollections" + }, + { + "path": "WorkingWithRanges" + }, + { + "path": "WorkingWithNetworkResources" + }, + { + "path": "Ch08Ex02RegularExpressions" + }, + { + "path": "Ch08Ex03NumbersAsWordsLib" + }, + { + "path": "Ch08Ex03NumbersAsWordsApp" + }, + { + "path": "Ch08Ex03NumbersAsWordsTests" + } + ] +} \ No newline at end of file diff --git a/vscode/Chapter08/WorkingWithCollections/Program.Helpers.cs b/vscode/Chapter08/WorkingWithCollections/Program.Helpers.cs new file mode 100644 index 0000000..a31041c --- /dev/null +++ b/vscode/Chapter08/WorkingWithCollections/Program.Helpers.cs @@ -0,0 +1,21 @@ +partial class Program +{ + static void Output(string title, IEnumerable collection) + { + WriteLine(title); + foreach (string item in collection) + { + WriteLine($" {item}"); + } + } + + static void OutputPQ(string title, + IEnumerable<(TElement Element, TPriority Priority)> collection) + { + WriteLine(title); + foreach ((TElement, TPriority) item in collection) + { + WriteLine($" {item.Item1}: {item.Item2}"); + } + } +} diff --git a/vscode/Chapter08/WorkingWithCollections/Program.cs b/vscode/Chapter08/WorkingWithCollections/Program.cs new file mode 100644 index 0000000..599e08e --- /dev/null +++ b/vscode/Chapter08/WorkingWithCollections/Program.cs @@ -0,0 +1,129 @@ +using System.Collections.Immutable; + +// Working with lists + +// Simple syntax for creating a list and adding three items +List cities = new(); +cities.Add("London"); +cities.Add("Paris"); +cities.Add("Milan"); + +/* Alternative syntax that is converted by the compiler into + the three Add method calls above +List cities = new() + { "London", "Paris", "Milan" }; */ + +/* Alternative syntax that passes an + array of string values to AddRange method +List cities = new(); +cities.AddRange(new[] { "London", "Paris", "Milan" }); */ + +Output("Initial list", cities); +WriteLine($"The first city is {cities[0]}."); +WriteLine($"The last city is {cities[cities.Count - 1]}."); + +cities.Insert(0, "Sydney"); +Output("After inserting Sydney at index 0", cities); + +cities.RemoveAt(1); +cities.Remove("Milan"); +Output("After removing two cities", cities); + +// Working with dictionaries + +Dictionary keywords = new(); + +// add using named parameters +keywords.Add(key: "int", value: "32-bit integer data type"); + +// add using positional parameters +keywords.Add("long", "64-bit integer data type"); +keywords.Add("float", "Single precision floating point number"); + +/* Alternative syntax; compiler converts this to calls to Add method +Dictionary keywords = new() +{ + { "int", "32-bit integer data type" }, + { "long", "64-bit integer data type" }, + { "float", "Single precision floating point number" }, +}; */ + +/* Alternative syntax; compiler converts this to calls to Add method +Dictionary keywords = new() +{ + ["int"] = "32-bit integer data type", + ["long"] = "64-bit integer data type", + ["float"] = "Single precision floating point number", // last comma is optional +}; */ + +Output("Dictionary keys:", keywords.Keys); +Output("Dictionary values:", keywords.Values); + +WriteLine("Keywords and their definitions"); +foreach (KeyValuePair item in keywords) +{ + WriteLine($" {item.Key}: {item.Value}"); +} + +// lookup a value using a key +string key = "long"; +WriteLine($"The definition of {key} is {keywords[key]}"); + +// Working with queues + +Queue coffee = new(); + +coffee.Enqueue("Damir"); // front of queue +coffee.Enqueue("Andrea"); +coffee.Enqueue("Ronald"); +coffee.Enqueue("Amin"); +coffee.Enqueue("Irina"); // back of queue + +Output("Initial queue from front to back", coffee); + +// server handles next person in queue +string served = coffee.Dequeue(); +WriteLine($"Served: {served}."); + +// server handles next person in queue +served = coffee.Dequeue(); +WriteLine($"Served: {served}."); +Output("Current queue from front to back", coffee); + +WriteLine($"{coffee.Peek()} is next in line."); +Output("Current queue from front to back", coffee); + +// Working with priority queues + +PriorityQueue vaccine = new(); + +// add some people +// 1 = high priority people in their 70s or poor health +// 2 = medium priority e.g. middle aged +// 3 = low priority e.g. teens and twenties + +vaccine.Enqueue("Pamela", 1); // my mum (70s) +vaccine.Enqueue("Rebecca", 3); // my niece (teens) +vaccine.Enqueue("Juliet", 2); // my sister (40s) +vaccine.Enqueue("Ian", 1); // my dad (70s) + +OutputPQ("Current queue for vaccination:", vaccine.UnorderedItems); + +WriteLine($"{vaccine.Dequeue()} has been vaccinated."); +WriteLine($"{vaccine.Dequeue()} has been vaccinated."); +OutputPQ("Current queue for vaccination:", vaccine.UnorderedItems); + +WriteLine($"{vaccine.Dequeue()} has been vaccinated."); + +WriteLine("Adding Mark to queue with priority 2"); +vaccine.Enqueue("Mark", 2); // me (50s) + +WriteLine($"{vaccine.Peek()} will be next to be vaccinated."); +OutputPQ("Current queue for vaccination:", vaccine.UnorderedItems); + +// Working with immutable collections + +ImmutableList immutableCities = cities.ToImmutableList(); +ImmutableList newList = immutableCities.Add("Rio"); +Output("Immutable list of cities:", immutableCities); +Output("New list of cities:", newList); diff --git a/vscode/Chapter08/WorkingWithCollections/WorkingWithCollections.csproj b/vscode/Chapter08/WorkingWithCollections/WorkingWithCollections.csproj new file mode 100644 index 0000000..cd63b28 --- /dev/null +++ b/vscode/Chapter08/WorkingWithCollections/WorkingWithCollections.csproj @@ -0,0 +1,14 @@ + + + + Exe + net7.0 + enable + enable + + + + + + + diff --git a/vscode/Chapter08/WorkingWithNetworkResources/Program.cs b/vscode/Chapter08/WorkingWithNetworkResources/Program.cs new file mode 100644 index 0000000..e614734 --- /dev/null +++ b/vscode/Chapter08/WorkingWithNetworkResources/Program.cs @@ -0,0 +1,50 @@ +using System.Net; // IPHostEntry, Dns, IPAddress +using System.Net.NetworkInformation; // Ping, PingReply, IPStatus + +// Working with URIs, DNS, and IP addresses + +Write("Enter a valid web address (or press Enter): "); +string? url = ReadLine(); + +if (string.IsNullOrWhiteSpace(url)) // if they enter nothing... +{ + // ... set a default URL + url = "https://stackoverflow.com/search?q=securestring"; +} + +Uri uri = new(url); +WriteLine($"URL: {url}"); +WriteLine($"Scheme: {uri.Scheme}"); +WriteLine($"Port: {uri.Port}"); +WriteLine($"Host: {uri.Host}"); +WriteLine($"Path: {uri.AbsolutePath}"); +WriteLine($"Query: {uri.Query}"); + +IPHostEntry entry = Dns.GetHostEntry(uri.Host); +WriteLine($"{entry.HostName} has the following IP addresses:"); +foreach (IPAddress address in entry.AddressList) +{ + WriteLine($" {address} ({address.AddressFamily})"); +} + +// Pinging a server + +try +{ + Ping ping = new(); + + WriteLine("Pinging server. Please wait..."); + PingReply reply = ping.Send(uri.Host); + WriteLine($"{uri.Host} was pinged and replied: {reply.Status}."); + + if (reply.Status == IPStatus.Success) + { + WriteLine("Reply from {0} took {1:N0}ms", + arg0: reply.Address, + arg1: reply.RoundtripTime); + } +} +catch (Exception ex) +{ + WriteLine($"{ex.GetType().ToString()} says {ex.Message}"); +} diff --git a/vscode/Chapter08/WorkingWithNetworkResources/WorkingWithNetworkResources.csproj b/vscode/Chapter08/WorkingWithNetworkResources/WorkingWithNetworkResources.csproj new file mode 100644 index 0000000..91807b2 --- /dev/null +++ b/vscode/Chapter08/WorkingWithNetworkResources/WorkingWithNetworkResources.csproj @@ -0,0 +1,14 @@ + + + + Exe + net7.0 + enable + enable + + + + + + + diff --git a/vscode/Chapter08/WorkingWithNumbers/Program.cs b/vscode/Chapter08/WorkingWithNumbers/Program.cs new file mode 100644 index 0000000..7985a1e --- /dev/null +++ b/vscode/Chapter08/WorkingWithNumbers/Program.cs @@ -0,0 +1,27 @@ +using System.Numerics; + +WriteLine("Working with large integers:"); +WriteLine("-----------------------------------"); + +ulong big = ulong.MaxValue; +WriteLine($"{big,40:N0}"); + +BigInteger bigger = + BigInteger.Parse("123456789012345678901234567890"); +WriteLine($"{bigger,40:N0}"); + +WriteLine("Working with complex numbers:"); + +Complex c1 = new(real: 4, imaginary: 2); +Complex c2 = new(real: 3, imaginary: 7); +Complex c3 = c1 + c2; + +// output using default ToString implementation +WriteLine($"{c1} added to {c2} is {c3}"); + +// output using custom format +WriteLine("{0} + {1}i added to {2} + {3}i is {4} + {5}i", + c1.Real, c1.Imaginary, + c2.Real, c2.Imaginary, + c3.Real, c3.Imaginary); + diff --git a/vscode/Chapter08/WorkingWithNumbers/WorkingWithNumbers.csproj b/vscode/Chapter08/WorkingWithNumbers/WorkingWithNumbers.csproj new file mode 100644 index 0000000..cd63b28 --- /dev/null +++ b/vscode/Chapter08/WorkingWithNumbers/WorkingWithNumbers.csproj @@ -0,0 +1,14 @@ + + + + Exe + net7.0 + enable + enable + + + + + + + diff --git a/vscode/Chapter08/WorkingWithRanges/Program.cs b/vscode/Chapter08/WorkingWithRanges/Program.cs new file mode 100644 index 0000000..584461f --- /dev/null +++ b/vscode/Chapter08/WorkingWithRanges/Program.cs @@ -0,0 +1,28 @@ +string name = "Samantha Jones"; + +// getting the lengths of the first and last names + +int lengthOfFirst = name.IndexOf(' '); +int lengthOfLast = name.Length - lengthOfFirst - 1; + +// Using Substring + +string firstName = name.Substring( + startIndex: 0, + length: lengthOfFirst); + +string lastName = name.Substring( + startIndex: name.Length - lengthOfLast, + length: lengthOfLast); + +WriteLine($"First name: {firstName}, Last name: {lastName}"); + +// Using spans + +ReadOnlySpan nameAsSpan = name.AsSpan(); +ReadOnlySpan firstNameSpan = nameAsSpan[0..lengthOfFirst]; +ReadOnlySpan lastNameSpan = nameAsSpan[^lengthOfLast..^0]; + +WriteLine("First name: {0}, Last name: {1}", + arg0: firstNameSpan.ToString(), + arg1: lastNameSpan.ToString()); diff --git a/vscode/Chapter08/WorkingWithRanges/WorkingWithRanges.csproj b/vscode/Chapter08/WorkingWithRanges/WorkingWithRanges.csproj new file mode 100644 index 0000000..cd63b28 --- /dev/null +++ b/vscode/Chapter08/WorkingWithRanges/WorkingWithRanges.csproj @@ -0,0 +1,14 @@ + + + + Exe + net7.0 + enable + enable + + + + + + + diff --git a/vscode/Chapter08/WorkingWithRegularExpressions/Program.cs b/vscode/Chapter08/WorkingWithRegularExpressions/Program.cs new file mode 100644 index 0000000..434bbd8 --- /dev/null +++ b/vscode/Chapter08/WorkingWithRegularExpressions/Program.cs @@ -0,0 +1,38 @@ +using System.Text.RegularExpressions; // Regex + +Write("Enter your age: "); +string input = ReadLine()!; // null-forgiving + +Regex ageChecker = new(@"^\d+$"); + +if (ageChecker.IsMatch(input)) +{ + WriteLine("Thank you!"); +} +else +{ + WriteLine($"This is not a valid age: {input}"); +} + +string films = "\"Monsters, Inc.\",\"I, Tonya\",\"Lock, Stock and Two Smoking Barrels\""; + +WriteLine($"Films to split: {films}"); + +string[] filmsDumb = films.Split(','); + +WriteLine("Splitting with string.Split method:"); +foreach (string film in filmsDumb) +{ + WriteLine(film); +} + +Regex csv = new( + "(?:^|,)(?=[^\"]|(\")?)\"?((?(1)[^\"]*|[^,\"]*))\"?(?=,|$)"); + +MatchCollection filmsSmart = csv.Matches(films); + +WriteLine("Splitting with regular expression:"); +foreach (Match film in filmsSmart) +{ + WriteLine(film.Groups[2].Value); +} diff --git a/vscode/Chapter08/WorkingWithRegularExpressions/WorkingWithRegularExpressions.csproj b/vscode/Chapter08/WorkingWithRegularExpressions/WorkingWithRegularExpressions.csproj new file mode 100644 index 0000000..cd63b28 --- /dev/null +++ b/vscode/Chapter08/WorkingWithRegularExpressions/WorkingWithRegularExpressions.csproj @@ -0,0 +1,14 @@ + + + + Exe + net7.0 + enable + enable + + + + + + + diff --git a/vscode/Chapter08/WorkingWithText/Program.cs b/vscode/Chapter08/WorkingWithText/Program.cs new file mode 100644 index 0000000..139e2ed --- /dev/null +++ b/vscode/Chapter08/WorkingWithText/Program.cs @@ -0,0 +1,45 @@ +string city = "London"; +WriteLine($"{city} is {city.Length} characters long."); + +WriteLine($"First char is {city[0]} and third is {city[2]}."); + +string cities = "Paris,Tehran,Chennai,Sydney,New York,Medellín"; + +string[] citiesArray = cities.Split(','); + +WriteLine($"There are {citiesArray.Length} items in the array."); + +foreach (string item in citiesArray) +{ + WriteLine(item); +} + +string fullName = "Alan Jones"; + +int indexOfTheSpace = fullName.IndexOf(' '); + +string firstName = fullName.Substring( + startIndex: 0, length: indexOfTheSpace); + +string lastName = fullName.Substring( + startIndex: indexOfTheSpace + 1); + +WriteLine($"Original: {fullName}"); +WriteLine($"Swapped: {lastName}, {firstName}"); + +string company = "Microsoft"; +bool startsWithM = company.StartsWith("M"); +bool containsN = company.Contains("N"); +WriteLine($"Text: {company}"); +WriteLine($"Starts with M: {startsWithM}, contains an N: {containsN}"); + +string recombined = string.Join(" => ", citiesArray); +WriteLine(recombined); + +string fruit = "Apples"; +decimal price = 0.39M; +DateTime when = DateTime.Today; + +WriteLine($"Interpolated: {fruit} cost {price:C} on {when:dddd}."); +WriteLine(string.Format("string.Format: {0} cost {1:C} on {2:dddd}.", + arg0: fruit, arg1: price, arg2: when)); diff --git a/vscode/Chapter08/WorkingWithText/WorkingWithText.csproj b/vscode/Chapter08/WorkingWithText/WorkingWithText.csproj new file mode 100644 index 0000000..cd63b28 --- /dev/null +++ b/vscode/Chapter08/WorkingWithText/WorkingWithText.csproj @@ -0,0 +1,14 @@ + + + + Exe + net7.0 + enable + enable + + + + + + +