ms_learn_csharp/023_alphanumeric_data_format/023_csharp.md
2024-08-01 22:51:46 -04:00

27 KiB

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:

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.

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:

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:

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:

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:

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:

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.

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 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.

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:

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.

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:

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.

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:

decimal tax = .36785m;
Console.WriteLine($"Tax rate: {tax:P2}");

If you're viewing this from the en-US culture, you observe the following output.

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.

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.

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.

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.

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:

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:

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:

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:

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:

Console.WriteLine($"     Sub Total: {subtotal:C}");

When you run the code, you should see the following output:

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:

Console.WriteLine($"           Tax: {taxPercentage:P2}");

When you run the code, you should see the following output:

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:

Console.WriteLine($"     Total Billed: {total:C}");

The entire code for the exercise should match as follows:

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:

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:

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:

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.

    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:

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:

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.

 ----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:

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:

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:

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:

formattedLine = formattedLine + payeeName.PadRight(24);

Save your code file, and then run your code. You should see the following output:

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:

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:

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.

Console.WriteLine("1234567890123456789012345678901234567890");

Update your code in the Visual Studio Code Editor as follows:

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:

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).

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:

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:

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 Standard Numeric Format Strings