ms_learn_csharp/023_alphanumeric_data_format/023_csharp.md

833 lines
27 KiB
Markdown
Raw Normal View History

2024-08-01 22:51:46 -04:00
# Format alphanumeric data for presentation in C#
## Introduction
Suppose you work for a sales and marketing department that sends thousands of
personalized letters to the company's existing clients who are institutional
investors. Your team's job is to promote new financial products to the customer.
Each letter you send merges personalized information about the customer. The
letter compares the returns of their current portfolios with projected returns
using the newest products. How do you merge and format the data correctly?
From a high-level perspective, software developers are concerned with:
- **data input**, including data typed in by a user from a keyboard, using
their mouse, a device, or by another software system via a network request.
- **data processing**, including decision logic, manipulating data, and
performing calculations.
- **data output**, including presentation to an end user via a command-line
message, a window, a web page, or saving the processed data into a file, and
sending it to a network service.
To solve business problems in C#, you need to work with different types of data,
such as strings and numbers. You also need to perform various operations on the
data, such as calculations, comparisons, or conversions. In this module, you
output string and numeric data in C# using various formatting options. You also
create a receipt mockup and a personalized marketing message using data merging
techniques.
### Learning objectives
In this module, you will:
- Merge string templates with variables using composite formatting.
- Use various format specifiers to properly display percentages, currency, and
numbers.
- Use padding methods to properly align string values.
---
## Exercise - Investigate string formatting basics
In this unit, you learn methods to format strings for efficient display,
especially for cases using multiple variables.
### What is Composite Formatting?
*Composite formatting* uses numbered placeholders within a string. At run time,
everything inside the braces is resolved to a value that is also passed in
based on their position.
This example of composite formatting uses a built-in method `Format()` on the
`string` data type keyword. Update your code in the editor as follows:
```cs
string first = "Hello";
string second = "World";
string result = string.Format("{0} {1}!", first, second);
Console.WriteLine(result);
```
If you run this code, you observe the following output.
```txt
Hello World!
```
There are a few important things to notice about this code.
- Data types and variables of a given data type have built-in "helper methods"
to make certain tasks easy.
- The literal string `"{0} {1}!"` forms a template, parts of which are replaced
at run time.
- The token `{0}` is replaced by the first argument after the string template,
in other words, the value of the variable `first`.
- The token `{1}` is replaced by the second argument after the string template,
in other words, the value of the variable `second`.
> Note
> You may think it's odd to start with the number 0. Actually this is very
common in software development. Whenever there's a sequence of items that can
be identified using a number, the numbering will usually start at 0.
Update your code as follows:
```cs
string first = "Hello";
string second = "World";
Console.WriteLine("{1} {0}!", first, second);
Console.WriteLine("{0} {0} {0}!", first, second);
```
Save your code file, and then run your code. You should see the following
output:
```txt
World Hello!
Hello Hello Hello!
```
A few observations about these examples:
- For the first `Console.WriteLine()` statement, observe that the tokens can be
arranged in any order. The sample code has `{1}` before `{0}`.
- For the second `Console.WriteLine()` statement, observe that the tokens can be
reused with three instances of `{0}`. Also, the second variable argument,
`second`, isn't used. Yet, the code still runs without error.
#### What is string interpolation?
*String interpolation* is a technique that simplifies composite formatting.
Instead of using a numbered token and including the literal value or variable
name in a list of arguments to `String.Format()` or `Console.WriteLine()`, you
can just use the variable name inside of the curly braces.
In order for a string to be interpolated, you must prefix it with the `$`
directive. Now, create the same examples from earlier using string interpolation
instead of composite formatting. Update your code as follows:
```cs
string first = "Hello";
string second = "World";
Console.WriteLine($"{first} {second}!");
Console.WriteLine($"{second} {first}!");
Console.WriteLine($"{first} {first} {first}!");
```
Save your code file, and then run your code. You should see the following
output:
```txt
Hello World!
World Hello!
Hello Hello Hello!
```
> Note
> If you look at code examples in books and online, you're likely to see both
*composite formatting* and *string interpolation* used, but generally you should
choose *string interpolation*.
#### Formatting currency
Composite formatting and string interpolation can be used to format values for
display given a specific language and culture. In the following example, the
`:C` currency format specifier is used to present the `price` and `discount`
variables as currency. Update your code as follows:
```cs
decimal price = 123.45m;
int discount = 50;
Console.WriteLine($"Price: {price:C} (Save {discount:C})");
```
If you executed this code on a computer that has its Windows display language
set to "English (United States)", you observe the following output.
```txt
Price: $123.45 (Save $50.00)
```
Notice how adding the `:C` to the tokens inside of the curly braces formats the
number as currency regardless of whether you use `int` or `decimal`.
> Note
> What happens if your country/region and language isn't known? If you run the
previous code in the "in-browser" .NET Editor, such as at
[TrydotNet](https://dotnet.microsoft.com/en-us/platform/try-dotnet) you'll see
the following output: `Price: ¤123.45 (Save ¤50.00)`. The symbol `¤` is used
instead of the symbol for your country/region's money. This is a generic symbol
used to denote "currency" regardless of the *type* of currency. You see this
symbol in the .NET Editor because it ignores your current location.
### How the user's country/region and language affect string formatting
What if you execute the previous code on a computer in France that has its
Windows Display Language set to French? In that case you would see the
following output.
```txt
Price: 123,45 € (Save 50,00 €)
```
The reason for the previous "€" output is that the string currency formatting
feature is dependent on the local computer setting for culture. In this context,
the term "culture" refers to the country/region and language of the end user.
The culture code is a five character string that computers use to identify the
location and language of the end user. The culture code ensures certain
information like dates and currency can be presented properly.
For example:
- the culture code of an English speaker in the USA is `en-US`.
- the culture code of a French speaker in France is `fr-FR`.
- the culture code of a French speaker in Canada is `fr-CA`.
The culture affects the writing system, the calendar that's used, the sort
order of strings, and formatting for dates and numbers (like formatting
currency).
Unfortunately, making sure your code works correctly on all computers
regardless of the country/region or the end user's language is challenging.
This process is known as *localization* (or *globalization*). Localization
depends on many factors not discussed in this module, but simply, the string
formatting syntax might use a different format depending on the user's culture.
#### Formatting numbers
When working with numeric data, you might want to format the number for
readability by including commas to delineate thousands, millions, billions, and
so on.
The `N` numeric format specifier makes numbers more readable. Update your code
as follows:
```txt
decimal measurement = 123456.78912m;
Console.WriteLine($"Measurement: {measurement:N} units");
```
If you're viewing this from the `en-US` culture, you observe the following
output.
```txt
Measurement: 123,456.79 units
```
By default, the `N` numeric format specifier displays only two digits after the
decimal point.
If you want to display more precision, you can do that by adding a number after
the specifier. The following code will display four digits after the decimal
point using the `N4` specifier. Update your code as follows:
```cs
decimal measurement = 123456.78912m;
Console.WriteLine($"Measurement: {measurement:N4} units");
```
If you're viewing this from the `en-US` culture, you observe the following
output.
```txt
Measurement: 123,456.7891 units
```
#### Formatting percentages
Use the `P` format specifier to format percentages and rounds to 2 decimal
places. Add a number afterwards to control the number of values displayed after
the decimal point. Update your code as follows:
```cs
decimal tax = .36785m;
Console.WriteLine($"Tax rate: {tax:P2}");
```
If you're viewing this from the `en-US` culture, you observe the following
output.
```txt
Tax rate: 36.79%
```
#### Combining formatting approaches
String variables can store strings created using formatting techniques. In the
following example, decimals and decimal math results are formatted and stored
in the `yourDiscount` string using composite formatting.
Update your code as follows.
```cs
decimal price = 67.55m;
decimal salePrice = 59.99m;
string yourDiscount = String.Format("You saved {0:C2} off the regular {1:C2} price. ", (price - salePrice), price);
Console.WriteLine(yourDiscount);
```
If you're viewing this from the `en-US` culture, you observe the following
output.
```txt
You saved $7.56 off the regular $67.55 price.
```
You can combine multiple formatted strings. Build on the previous code
concatenating the calculated percentage using the string interpolation instead
of string concatenation by inserting
`yourDiscount += $"A discount of {(price - salePrice)/price:P2}!";` into the
code on the line before `Console.WriteLine()`.
> Note
> You don't need to use `String.Format()` with this string interpolation
approach.
Update your code as follows.
```cs
decimal price = 67.55m;
decimal salePrice = 59.99m;
string yourDiscount = String.Format("You saved {0:C2} off the regular {1:C2} price. ", (price - salePrice), price);
yourDiscount += $"A discount of {((price - salePrice)/price):P2}!"; //inserted
Console.WriteLine(yourDiscount);
```
If you're viewing this unit from the `en-US` culture, you observe the following
output.
```txt
You saved $7.56 off the regular $67.55 price. A discount of 11.19%!
```
### Recap
Here are most important takeaways from this unit about string formatting:
- You can use composite formatting or string interpolation to format strings.
- With **composite formatting**, you use a string template containing one or
more replacement tokens in the form `{0}`. You also supply a list of arguments
that are matched with the replacement tokens based on their order. Composite
formatting works when using `string.Format()` or `Console.WriteLine()`.
- With **string interpolation**, you use a string template containing the
variable names you want replaced surrounded by curly braces. Use the `$`
directive before the string template to indicate you want the string to be
interpolated.
- Format currency using a `:C` specifier.
- Format numbers using a `:N` specifier. Control the precision (number of values
after the decimal point) using a number after the `:N` like `{myNumber:N3}`.
- Format percentages using the `:P` format specifier.
- Formatting currency and numbers depend on the end user's culture, a five
character code that includes the user's country/region and language (per the
settings on their computer).
---
## Exercise
### Explore string interpolation
You need to create the code to print a receipt for the customer purchasing
shares of an investment product. The shares are purchased automatically at the
end of the year based on a series of payroll deductions, so the number of
shares purchased usually contains a decimal amount. To print the receipt, you
would likely need to combine data of different types, including fractional
values, currency, and percentages in precise ways.
### Display the invoice number using string interpolation
Update your code in the editor as follows:
```cs
int invoiceNumber = 1201;
decimal productShares = 25.4568m;
decimal subtotal = 2750.00m;
decimal taxPercentage = .15825m;
decimal total = 3185.19m;
Console.WriteLine($"Invoice Number: {invoiceNumber}");
```
The Program.cs file must be saved before building or running the code.
At the Terminal command prompt, to run your code, type `dotnet run` and then
press Enter.
You should see the following output:
```txt
Invoice Number: 1201
```
> Note
> You may see several warnings such as `warning CS0219: The variable
'productShares' is assigned but its value is never used` for all the variables
that were defined but not yet used in the code.
### Display the product shares with one thousandth of a share (0.001) precision
Since you bill the customers using fractions of shares even though the precision
is one ten thousandth (0.0001), you'll only display three digits after the
decimal point.
Add the following code below the code you typed previously:
```cs
Console.WriteLine($" Shares: {productShares:N3} Product");
```
Save your code file, and then run your code.
When you run the code, you should see the following output:
```txt
Invoice Number: 1201
Shares: 25.457 Product
```
### Display the subtotal that you charge the customer formatted as currency
Add the following code below the code you typed in steps 1 and 2:
```cs
Console.WriteLine($" Sub Total: {subtotal:C}");
```
When you run the code, you should see the following output:
```txt
Invoice Number: 1201
Shares: 25.457 Product
Sub Total: $2,750.00
```
> Note
> The sample shows the "$" but you may see a different regional currency symbol.
### Display the tax charged on the sale formatted as a percentage
Add the following code below the code you typed in steps 1 through 3:
```cs
Console.WriteLine($" Tax: {taxPercentage:P2}");
```
When you run the code, you should see the following output:
```txt
Invoice Number: 1201
Shares: 25.457 Product
Sub Total: $2,750.00
Tax: 15.83%
```
### Finalize the receipt with the total amount due formatted as currency
Add the following code below the code you typed in steps 1 through 4:
```cs
Console.WriteLine($" Total Billed: {total:C}");
```
The entire code for the exercise should match as follows:
```cs
int invoiceNumber = 1201;
decimal productShares = 25.4568m;
decimal subtotal = 2750.00m;
decimal taxPercentage = .15825m;
decimal total = 3185.19m;
Console.WriteLine($"Invoice Number: {invoiceNumber}");
Console.WriteLine($" Shares: {productShares:N3} Product");
Console.WriteLine($" Sub Total: {subtotal:C}");
Console.WriteLine($" Tax: {taxPercentage:P2}");
Console.WriteLine($" Total Billed: {total:C}");
```
When you run the code, you should see the following output:
```txt
Invoice Number: 1201
Shares: 25.457 Product
Sub Total: $2,750.00
Tax: 15.83%
Total Billed: $3,185.19
```
---
## Exercise
### Discover padding and alignment
The `string.Format()` method is used to perform composite formatting such as in
the example:
```cs
string first = "Hello";
string second = "World";
string result = string.Format("{0} {1}!", first, second);
Console.WriteLine(result);
```
It may have seemed a bit strange that a keyword that represents a data type has
methods you can call in the same way that you call methods on the `Console`
class. The fact is that there are many similar methods on the `string` data type
and any literal string or variable of type string.
Here's a brief list of categories of these built-in methods so you can get an
idea of what's possible.
- Methods that add blank spaces for formatting purposes (`PadLeft()`,
`PadRight()`)
- Methods that compare two strings or facilitate comparison (`Trim()`,
`TrimStart()`, `TrimEnd()`, `GetHashcode()`, the `Length` property)
- Methods that help you determine what's inside of a string, or even retrieve
just a part of the string (`Contains()`, `StartsWith()`, `EndsWith()`,
`Substring()`)
- Methods that change the content of the string by replacing, inserting, or
removing parts (`Replace()`, `Insert()`, `Remove()`)
- Methods that turn a string into an array of strings or characters (`Split()`,
`ToCharArray()`)
### Formatting strings by adding whitespace before or after
The `PadLeft()` method adds blank spaces to the left-hand side of the string so
that the total number of characters equals the argument you send it. In this
case, you want the total length of the string to be 12 characters.
Update your code in the editor as follows:
```cs
string input = "Pad this";
Console.WriteLine(input.PadLeft(12));
```
At the Terminal command prompt, to run your code, type `dotnet run` and then
press Enter.
When you run the code, you observe four characters prefixed to the left of the
string bring the length to 12 characters long.
```txt
Pad this
```
To add space or characters to the right side of your string, use the
`PadRight()` method instead. 1. Update your code in the editor as follows:
```cs
Console.WriteLine(input.PadRight(12));
```
Save your code file, and then run your code. You won't observe any characters
added to the end of the string, but they're there.
#### What is an overloaded method?
In C#, an overloaded method is another version of a method with different or
extra arguments that modify the functionality of the method slightly, as is the
case with the overloaded version of the `PadLeft()` method.
You can also call a second overloaded version of the method and pass in
whatever character you want to use instead of a space. In this case, you fill
the extra space with the dash character.
Update your code in the editor as follows:
```cs
Console.WriteLine(input.PadLeft(12, '-'));
Console.WriteLine(input.PadRight(12, '-'));
```
Save your code file, and then run your code. You should see four dashes
prefixing the left of the string that is 12 characters long.
```txt
----Pad this
Pad this----
```
Now, apply this newfound knowledge to another real world scenario.
### Working with padded strings
Suppose you work for a payment processing company that still supports legacy
mainframe systems. Often, those systems require data to be input in specific
columns. For example, store the Payment ID in columns 1 through 6, the payee's
name in columns 7 through 30, and the Payment Amount in columns 31 through 40.
Also, importantly, the Payment Amount is right-aligned.
You're asked to build an application that will convert data in the relational
database management system to the legacy file format. To ensure that the
integration works correctly, the first step is to confirm the file format by
giving the legacy system maintainers a sample of the output. Later, you build
on this work to send hundreds or thousands of payments to be processed via an
ASCII text file.
#### Add the Payment ID to the output
To get started, print the Payment ID in the first six columns. You pick some
random payment data that should be adequate for the purposes.
Update your code in the editor as follows:
```cs
string paymentId = "769C";
var formattedLine = paymentId.PadRight(6);
Console.WriteLine(formattedLine);
```
Reuse the `formattedLine` variable to build the output string.
Save your code file, and then run your code. You should see the following
output:
```txt
769C
```
There are two blank spaces to the right that not visible. You'll confirm that
they exist in the next step.
#### Add the payee name to the output
Next, you add a fictitious Payee Name, padding it appropriately.
Update your code as follows:
```cs
string paymentId = "769C";
string payeeName = "Mr. Stephen Ortega";
var formattedLine = paymentId.PadRight(6);
formattedLine += payeeName.PadRight(24);
Console.WriteLine(formattedLine);
```
The `+=` operator performs a string concatenation, taking the previous value of
the variable `formattedLine` and adding the new value to it. It's a shortened
equivalent the following code example:
```cs
formattedLine = formattedLine + payeeName.PadRight(24);
```
Save your code file, and then run your code. You should see the following
output:
```txt
769C Mr. Stephen Ortega
```
Again, there are quite a few blank spaces after the Payee's Name. Also, there
are two blank spaces after the Payment ID from Step 1.
#### Add the payment amount to the output
Next, add a fictitious Payment Amount and make sure to use `PadLeft()` to right
-align the output.
Update your code as follows:
```cs
string paymentId = "769C";
string payeeName = "Mr. Stephen Ortega";
string paymentAmount = "$5,000.00";
var formattedLine = paymentId.PadRight(6);
formattedLine += payeeName.PadRight(24);
formattedLine += paymentAmount.PadLeft(10);
Console.WriteLine(formattedLine);
```
Save your code file, and then run your code. You should see the following
output:
```txt
769C Mr. Stephen Ortega $5,000.00
```
This output is pretty close to what you understood the legacy system
maintainers were looking for.
#### Add a line of numbers above the output to more easily confirm the result
Since it's difficult to count the exact columns where each data element appears,
you add a line directly above the output that helps you count the columns.
```cs
Console.WriteLine("1234567890123456789012345678901234567890");
```
Update your code in the Visual Studio Code Editor as follows:
```txt
string paymentId = "769C";
string payeeName = "Mr. Stephen Ortega";
string paymentAmount = "$5,000.00";
var formattedLine = paymentId.PadRight(6);
formattedLine += payeeName.PadRight(24);
formattedLine += paymentAmount.PadLeft(10);
Console.WriteLine("1234567890123456789012345678901234567890");
Console.WriteLine(formattedLine);
```
Save your code file, and then run your code. You should see the following
output, that you can send off to the maintainers of the legacy system to
confirm the new integration works correctly:
```txt
1234567890123456789012345678901234567890
769C Mr. Stephen Ortega $5,000.00
```
Success!
### Recap
There's a few important takeaways from this unit.
- The `string` data type, literal strings, and variables of type string each
implement many helper methods to format, modify, and perform other operations
on strings.
- The `PadLeft()` and `PadRight()` methods add white space (or optionally,
another character) to the total length of a string.
- Use `PadLeft()` to right-align a string.
- Some methods are overloaded, meaning they have multiple versions of the
method with different arguments that affect their functionality.
- The `+=` operator concatenates a new string on the right to the existing
string on the left.
---
## Exercise
### Complete a challenge to apply string interpolation to a form letter
For the sales and marketing company's newest investment products, you send
thousands of personalized letters to the company's existing clients. Your job
is to write C# code to merge personalized information about the customer. The
letter contains information like their existing portfolio and compares their
current returns to projected returns if they were to invest in using the new
products.
The writers have decided on the following example marketing message. Here's the
desired output (using fictitious customer account data).
```txt
Dear Ms. Barros,
As a customer of our Magic Yield offering we are excited to tell you about a new financial product that would dramatically increase your return.
Currently, you own 2,975,000.00 shares at a return of 12.75%.
Our new product, Glorious Future offers a return of 13.13%. Given your current volume, your potential profit would be ¤63,000,000.00.
Here's a quick comparison:
Magic Yield 12.75% $55,000,000.00
Glorious Future 13.13% $63,000,000.00
```
Use your new found knowledge of string formatting to build an application that
can merge and format the appropriate content given the previous example output.
Pay particular attention to the white space and make sure you accurately
represent this exact format using C#.
Add the following code to get the data for the challenge:
```cs
string customerName = "Ms. Barros";
string currentProduct = "Magic Yield";
int currentShares = 2975000;
decimal currentReturn = 0.1275m;
decimal currentProfit = 55000000.0m;
string newProduct = "Glorious Future";
decimal newReturn = 0.13125m;
decimal newProfit = 63000000.0m;
// Your logic here
Console.WriteLine("Here's a quick comparison:\n");
string comparisonMessage = "";
// Your logic here
Console.WriteLine(comparisonMessage);
```
Use the code editor to generate the message while using the given variables and
code.
Ensure your code outputs the following message:
```txt
Dear Ms. Barros,
As a customer of our Magic Yield offering we are excited to tell you about a new financial product that would dramatically increase your return.
Currently, you own 2,975,000.00 shares at a return of 12.75%.
Our new product, Glorious Future offers a return of 13.13%. Given your current volume, your potential profit would be $63,000,000.00.
Here's a quick comparison:
Magic Yield 12.75% $55,000,000.00
Glorious Future 13.13% $63,000,000.00
```
Good luck!
Whether you get stuck and need to peek at the solution or you finish
successfully, continue on to view a solution to this challenge.
---
### Summary
Your company needed to create a personalized marketing message to promote a new
financial product to your customers. Using C#, you were able to write code that
combined string and numeric data about your current customers.
You exercised different techniques for merging a string template with variables
using composite formatting and string interpolation. The various specifiers
allow you to properly format large numbers, currency, and percentages. The
built-in methods on variables of type string allowed you to add padding to left
and right align data.
Imagine how much extra work is required if you didn't have a wealth of string
and number formatting tools and techniques available to you in C#. You would
likely attempt to use string concatenation everywhere, possibly making it
difficult to maintain or update.
Techniques and tools for formatting strings and numeric data has a wide array
of applications. For example, you can use these techniques to present data for
display on screen and format data to transfer between disparate systems.
#### Resources
[String.Format Method](https://learn.microsoft.com/en-us/dotnet/api/system.string.format?view=net-7.0&preserve-view=true)
[Standard Numeric Format Strings](https://learn.microsoft.com/en-us/dotnet/standard/base-types/standard-numeric-format-strings)