C# Programming · Lesson 3
Pulling Strings
Compare, search, transform, parse, split, join, and format text with deliberate C# string operations.
Lesson purpose
Lesson 3 treats text as structured data. Students learn how immutable strings behave, how to compare and transform text, how to search and loop through characters, how to clean and parse console input, and how to format output. The final pattern is an explicit pipeline: read, clean, split, validate, interpret, and present.
Learning objectives
- Explain string immutability and the special support surrounding System.String.
- Select comparison rules for code-like text or human language.
- Transform, search, loop through, split, join, pad, and format strings.
- Read console input, trim it, validate numeric text, and use the result.
- Choose string operations or StringBuilder according to the workload.
1. Strings are immutable
string is an alias for System.String, a class. Double-quoted literals make construction concise, but string operations return new strings instead of changing existing text.
string original = "Jenny";
string upper = original.ToUpper();
Console.WriteLine(original); // Jenny
Console.WriteLine(upper); // JENNY
Calling original.ToUpper() and ignoring its return value leaves original unchanged. Store the returned value when transformed text is needed. This same contract applies to Trim, ToLower, and Substring.
2. Common operations
string course = "IST 2373";
int length = course.Length;
char first = course[0];
bool hasSpace = course.Contains(' ');
string department = course.Substring(0, 3);
Console.WriteLine($"{length} {first} {hasSpace} {department}");
Indexes begin at zero. Length counts characters, not the highest index. Substring requires a valid start and count, so validate assumptions before indexing or slicing.
3. Comparison policy and case
Code-like identifiers usually need predictable ordinal comparison. Human language may require culture-aware rules. For a command, use a case-insensitive ordinal comparison:
string raw = Console.ReadLine() ?? "";
string command = raw.Trim();
bool shouldRun = command.Equals(
"run",
StringComparison.OrdinalIgnoreCase);
String.Compare returns a negative value, zero, or a positive value according to ordering. Choose the rule from the meaning of the data. ToUpper and ToLower return new strings and are useful for display or normalization, but they are not automatically the right equality strategy.
4. Loop through characters
A foreach loop visits each character in order:
string code = "C#10";
foreach (char ch in code)
{
Console.WriteLine(ch);
}
Use the loop for individual-character questions. Use string methods for questions about the sequence as a whole.
5. Search contracts
Contains answers yes or no. IndexOf returns the first matching position or -1. IndexOfAny searches for the first character from a supplied set. IsNullOrEmpty and IsNullOrWhiteSpace address different input conditions.
string message = "Build succeeded";
bool found = message.Contains("succeed");
int position = message.IndexOf("succeed");
bool blank = string.IsNullOrWhiteSpace(message);
if (position != -1)
{
Console.WriteLine(message.Substring(position, 7));
}
Never test IndexOf(…) > 0 when position zero is valid. Test for -1 before using the index. Use IsNullOrWhiteSpace when spaces should count as no meaningful input.
6. Read, clean, validate, and parse input
Console input arrives as text. Handle possible null input and remove outer spaces before applying a rule:
Console.Write("Quantity: ");
string textValue = Console.ReadLine() ?? "";
if (int.TryParse(textValue.Trim(), out int quantity))
{
Console.WriteLine($"Accepted: {quantity}");
}
else
{
Console.WriteLine("Enter a whole number.");
}
TryParse returns a Boolean success flag and writes the converted value through its out variable. It avoids the unhandled exception risk of parsing uncertain input.
Split turns a delimited line into an array. Validate the length before using an index:
string rawRecord = Console.ReadLine() ?? "";
string[] parts = rawRecord.Split(',');
if (parts.Length == 2 &&
int.TryParse(parts[1].Trim(), out int seats))
{
string course = parts[0].Trim().ToUpper();
Console.WriteLine($"{course}: {seats:N0} seats");
}
Join assembles array elements with one separator:
string[] topics = { "types", "strings", "operators" };
string summary = string.Join(" | ", topics);
Console.WriteLine(summary);
7. Fixed-width output and formatting
Trim removes outer whitespace. PadLeft and PadRight add padding until a minimum width; they do not truncate longer text.
string label = "Seats".PadRight(12);
string count = "5".PadLeft(4);
Console.WriteLine(label + count);
String.Concat joins pieces without a separator. String.Format uses numbered placeholders and specifiers:
string line = String.Format(
"{0}: {1:C} | {2:N0} seats",
"IST 2373",
49.95m,
5);
Console.WriteLine(line);
Interpolation keeps labels and values together:
string course = "IST 2373";
decimal fee = 49.95m;
int seats = 5;
Console.WriteLine($"{course}: {fee:C} | {seats:N0} seats");
C formats currency and N0 formats a grouped number with no decimal places. Formatting changes presentation, not the stored value.
8. StringBuilder and common bugs
Use string for a small number of transformations. Use StringBuilder for repeated appends or edits that would otherwise create many temporary strings.
using System.Text;
StringBuilder report = new StringBuilder();
report.Append("COURSE");
report.Append(" | ");
report.Append("SEATS");
report.Append("\n");
report.Append("IST 2373");
report.Append(" | ");
report.Append(5);
Console.WriteLine(report.ToString());
Review the lesson’s bug patterns: ignored Trim return values, treating position zero as not found, parsing uncertain input with int.Parse, using string quotes for a char, and assuming a second Split field exists.
Classroom application
Have students build a course-record formatter. Read IST 2373,25, split and trim the fields, validate the seat count with TryParse, normalize the course code, and display a padded formatted result. Test blank input, a missing comma, and a nonnumeric seat count.
Common misconceptions
- String methods return new values; they do not mutate the original.
- Index zero is a valid search result.
- Contains and IndexOf answer different questions.
- Split returns an array.
- PadRight creates a minimum width and does not shorten text.
- Formatting does not parse input.
- TryParse validates and converts; it does not format.
- StringBuilder is for repeated edits, not every concatenation.
Lesson summary
Text becomes dependable data through explicit rules. Students can now choose comparison policies, transform and search strings, iterate through characters, clean and parse input, split and join records, format output, and select StringBuilder when repeated changes justify it.
Course Notes