Chapter 9 Stringing in the Key of C#In This Chapter Pulling and twisting a string — but you still can’t push it Parsing strings read into the program Formatting output strings manually o
Trang 1Chapter 9 Stringing in the Key of C#
In This Chapter
Pulling and twisting a string — but you still can’t push it
Parsing strings read into the program
Formatting output strings manually or using the String.Format()method
For many applications, you can treat a stringlike one of the built-invalue-type variable types such as intor char Certain operations thatare otherwise reserved for these intrinsic types are available to strings, asfollows:
int i = 1; // declare and initialize an int string s = “abc”; // declare and initialize a string
In other respects, shown as follows, a stringis treated like a user-definedclass:
string s1 = new String();
string s2 = “abcd”;
int nLengthOfString = s2.Length;
Which is it — a value-type or a class? In fact, Stringis a class for which C#offers special treatment because strings are so widely used in programs Forexample, the keyword stringis synonymous with the class name String, asshown in the following code:
String s1 = “abcd”; // assign a string literal to a String obj string s2 = s1; // assign a String obj to a string variable
In this example, s1is declared to be an object of class String(spelled with
an uppercase S), while s2is declared as a simple string(spelled with a
low-ercase s) However, the two assignments demonstrate that stringand
Stringare of the same (or compatible) types
In fact, this same property is true of the other intrinsic variable types, to amore limited extent Even the lowly inttype has a corresponding class
Int32, doublehas the class Double, and so on The distinction here is that
stringand Stringreally are the same thing
Trang 2Performing Common Operations
on a String
C# programmers perform more operations on strings than Beverly Hills plasticsurgeons do on Hollywood hopefuls Virtually every program uses the “addi-tion” operator that’s used on strings, as shown in the following example:
string sName = “Randy”;
Console.WriteLine(“His name is “ + sName); // means concatenate
The Stringclass provides this special operator However, the Stringclassalso provides other, more direct methods for manipulating strings You cansee the complete list by looking up “String class”in the Help Index
The union is indivisible, and so are stringsYou need to know at least one thing that you didn’t learn before the 6th grade:You can’t change a stringobject itself after it has been created Even though
I may speak of modifying a string, C# doesn’t have an operation that modifiesthe actual stringobject Plenty of operations appear to modify the string
that you’re working with, but they always return the modified stringas anew object, instead
For example, the operation “His name is “ + “Randy”changes neither ofthe two strings, but generates a third string, “His name is Randy” Oneside effect of this behavior is that you don’t have to worry about someonemodifying a string“out from under you.”
Consider the following simplistic example program:
// ModifyString - the methods provided by the class String do not // modify the object itself (s.ToUpper() does not // modify s; rather, it returns a new string that // has been converted)using System;
namespace ModifyString {
class Program {
public static void Main(string[] args) {
// create a student object Student s1 = new Student();
s1.sName = “Jenny”;
// now make a new object with the same name Student s2 = new Student();
s2.sName = s1.sName;
Trang 3// change the object itself because ToUpper() returns // a new string without modifying the original s2.sName = s1.sName.ToUpper();
Console.WriteLine(“s1 - {0}, s2 - {1}”, s1.sName, s2.sName);
// wait for user to acknowledge the results Console.WriteLine(“Press Enter to terminate ”);
Console.Read();
} } // Student - we just need a class with a string in it class Student
{ public string sName;
} }
The Studentobjects s1and s2are set up so that their sNamedata memberpoints to the same string data The call to the ToUpper()method converts thestring s1.sNameto all uppercase characters Normally, this would be a prob-lem because both s1and s2point to the same object However, ToUpper()
does not change sName— it creates a new, independent uppercase string
The following output of the program is simple:
s1 - Jenny, s2 - JENNY Press Enter to terminate
This property of strings is called immutability (meaning, unchangeability).
The immutability of strings is also important for stringconstants A stringsuch as “this is a string”is a form of a stringconstant, just like 1 is an
intconstant In the same way that I reuse my shirts to reduce the size of mywardrobe, a compiler may choose to combine all accesses to the single con-stant “this is a string” Reusing stringconstants can reduce the foot-print of the resulting program but would be impossible if a stringcould bemodified
Equality for all strings:
The Compare() methodNumerous operations treat a string as a single object — for example, the
Compare()method Compare(), with the following properties, compares twostrings as if they were numbers:
If the left-hand string is greater than the right string, Compare()returns a 1
If the left-hand string is less than the right string, it returns a –1.
If the two strings are equal, it returns a 0
Trang 4The algorithm works as follows when written in “notational C#” (that is, C#without all the details, also known as pseudocode):
compare(string s1, string s2) {
// loop through each character of the strings until // a character in one string is greater than the // corresponding character in the other string foreach character in the shorter string
if (s1’s character > s2’s character when treated as a number) return 1
if (s2’s character < s1’s character) return -1
// Okay, every letter matches, but if the string s1 is longer // then it’s greater
if s1 has more characters left return 1
// if s2 is longer, it’s greater
if s2 has more characters left return -1
// if every character matches and the two strings are the same // length, then they are “equal”
return 0 }
Thus, “abcd”is greater than “abbd”, and “abcde”is greater than “abcd”.More often than not, you don’t care whether one string is greater than theother, but only whether the two strings are equal
You do want to know which string is “bigger” when performing a sort
The Compare()operation returns a 0 when two strings are identical The lowing test program uses the equality feature of Compare()to perform a cer-tain operation when the program encounters a particular string or strings
fol-BuildASentenceprompts the user to enter lines of text Each line is nated to the previous line to build a single sentence This program exits if the
concate-user enters the word EXIT, exit, QUIT, or quit:
// BuildASentence - the following program constructs sentences // by concatenating user input until the user // enters one of the termination characters - //
// this program shows when you need to look for // string equality
using System;
namespace BuildASentence {
public class Program {
public static void Main(string[] args)
Trang 5Console.WriteLine(“Each line you enter will be “
+ “added to a sentence until you “ + “enter EXIT or QUIT”);
// ask the user for input; continue concatenating // the phrases input until the user enters exit or // quit (start with a null sentence)
string sSentence = “”;
for(;;) { // get the next line Console.WriteLine(“Enter a string “);
string sLine = Console.ReadLine();
// exit the loop if it’s a terminator
if (IsTerminateString(sLine)) {
break;
} // otherwise, add it to the sentence sSentence = String.Concat(sSentence, sLine);
// let the user know how she’s doing Console.WriteLine(“\nYou’ve entered: {0}”, sSentence);
} Console.WriteLine(“\nTotal sentence:\n{0}”, sSentence);
// wait for user to acknowledge the results Console.WriteLine(“Press Enter to terminate ”);
Console.Read();
} // IsTerminateString - return a true if the source // string is equal to any of the termination strings public static bool IsTerminateString(string source) {
string[] sTerms = {“EXIT”, “exit”, “QUIT”, “quit” };
// compare the string entered to each of the // legal exit commands
foreach(string sTerm in sTerms) {
// return a true if you have a match
if (String.Compare(source, sTerm) == 0) {
return true;
} } return false;
} } }
After prompting the user for what the program expects, the program creates
an empty initial sentence string sSentence From there, the program enters
an “infinite” loop
Trang 6The controls while(true)and for(;;)loop forever, or at least long enoughfor some internal breakor returnto break you out The two loops are equiv-alent, and in practice, you’ll see them both Looping is covered in Chapter 5.
BuildASentenceprompts the user to enter a line of text, which the programreads using the ReadLine()method Having read the line, the programchecks to see whether it is a terminator by using the IsTerminateString()
function, which I wrote for the job This function returns trueif sLineis one
of the terminator phrases and falseotherwise
By convention, the name of a function that checks a property and returns a
trueor falsestarts with Is, Has, Can, or some similar word In this case,the name of the function IsTerminateString()implies the question, “Is
sLinea terminate string?” Of course, this is a human convention only — C#doesn’t care
If sLineis not one of the terminate strings, it is concatenated to the end ofthe sentence using the String.Concat()function The program outputs theimmediate result just so the user can see what’s going on
The IsTerminateString()method defines an array of strings sTerms Eachmember of this array is one of the strings you’re looking for Any of thesestrings causes the program to return a true, which causes the program toquit faster than a programmer forced to write COBOL
The program must include both “EXIT”and “exit”because Compare()siders the two strings different by default (The way the program is written,
con-these are the only two ways to spell exit Strings such as “Exit”and “eXit”
would not be recognized as terminators.)The IsTerminateString()function loops through each of the strings in thearray of target strings If Compare()reports a match to any of the terminatephrases, the function returns a true If the function reaches the end of theloop without a match, the function returns a false
Iterating through an array is a great way to look for one of various possiblevalues
Here’s an example run of the BuildASentenceprogram:
Each line you entered will be added to a sentence until you enter EXIT or QUIT Enter a string
Programming with
You’ve entered: Programming with Enter a string
C# is fun
Trang 7I have flagged my input in bold to make the output easier to read.
Would you like your compares with or without case?
The Compare()method used within IsTerminateString()considers
“EXIT”and “exit”to be different strings However, the Compare()function
is overloaded with a second version that includes a third argument Thisargument indicates whether the comparison should ignore the letter case
A trueindicates “ignore.” (Chapter 7 discusses function overloading.)The following version of IsTerminateString()returns a truewhether thestring passed is uppercase, lowercase, or a combination of the two:
// IsTerminateString - return a true if the source string is equal // to any of the termination characters
public static bool IsTerminateString(string source) {
// indicate true if passed either exit or quit, // irrespective of case
return (String.Compare(“exit”, source, true) == 0) ||
(String.Compare(“quit”, source, true) == 0);
}
This version of IsTerminateString()is simpler than the previous loopingversion The function doesn’t need to worry about case, and it can use a singleconditional expression because it now has only two options to consider
This IsTerminateString()doesn’t even use an ifstatement The bool
expression returns the calculated value directly to the user — it gets the ifout
What if I want to switch case?
I almost hate to bring it up, but you can use the switch()control to look for
a particular string Usually, you use the switch()control to compare a
Trang 8counting number to some set of possible values; however, the switch()doeswork on stringobjects, as well The following version of IsTerminateString()uses the switch()control:
// IsTerminateString - return a true if the source // string is equal to any of the termination strings public static bool IsTerminateString(string source) {
switch(source) {
} }
This approach works because you’re comparing only a limited number ofstrings The for()loop offers a much more flexible approach for searchingfor string values Using the case-less Compare()gives the program greaterflexibility in understanding the user
Reading character input
A program can read from the keyboard one character at a time, but you have
to worry about newlines and so on An easier approach reads a string andthen parses the characters out of the string
Parsing characters out of a string is another topic I don’t like to mention for fearthat programmers will abuse this technique In some cases, programmers aretoo quick to jump down into the middle of a string and start pulling out whatthey find there This is particularly true of C++ programmers because that’s theonly way they could deal with strings, until the addition of a string class.Your programs can read strings as if they were arrays of characters usingeither the foreachcontrol or the index operator [] The following
StringToCharAccessprogram demonstrates this technique:
// StringToCharAccess - access the characters in a string // as if the string were an array using System;
namespace StringToCharAccess {
public class Program {
public static void Main(string[] args)
Trang 9// read a string in from the keyboard Console.WriteLine(“Input some random character string.”
+ “Make sure it’s completely random”);
string sRandom = Console.ReadLine();
// first output as a string Console.WriteLine(“When output as a string: “ + sRandom);
Console.WriteLine();
// now output as a series of characters Console.Write(“When output using the foreach: “);
foreach(char c in sRandom) {
Console.Write(c);
} Console.WriteLine(); // terminate the line // put a blank line divider
Console.Read();
} } }
This program first outputs some string picked totally at random In fact, Imay have read it somewhere or done a tap dance on the keyboard The pro-gram first outputs the string using the conventional WriteLine(string)
method It follows this by using the foreachto fetch each character in thestring, one at a time Finally, it uses a forloop with the array brackets []to
do the same thing The results are as follows:
Input some random character string Make sure it’s completely random Stephen Davis is one handsome individual
When output as a string: Stephen Davis is one handsome individual When output using the foreach: Stephen Davis is one handsome individual When output using the for: Stephen Davis is one handsome individual Press Enter to terminate
In some cases, you don’t want to mess with any white space on either end of
the string The term white space refers to the characters that don’t normally
display on the screen, for example, space, newline (or \n),and tab (\t)
Trang 10You can use the Trim()method to trim off the edges of the string as follows:
// get rid of any extra spaces on either end of the string sRandom = sRandom.Trim();
String.Trim()returns a new string The previous version of the string withthe extra white space is lost and no longer usable
Parsing numeric inputThe ReadLine()function used for reading from the console returns a string
object A program that expects numeric input must convert this string C#provides just the conversion tool you need in the Convertclass This classprovides a conversion method from stringto each built-in variable type.Thus, the following code segment reads a number from the keyboard andstores it into an intvariable:
string s = Console.ReadLine(); // keyboard input is string data int n = Convert.Int32(s); // but you know it’s meant to be a number
The other conversion methods are a bit more obvious: ToDouble(),
ToFloat(), and ToBoolean()
ToInt32()refers to a 32-bit, signed integer (32 bits is the size of a normal int),
so this is the conversion function for ints ToInt64()is the size of a long.When Convert()encounters an unexpected character type, it can generateunexpected results Thus, you must know for sure what type of data you’reprocessing (Bonus Chapter 1 on the CD covers dealing with unexpected
occurrences, called exceptions.)
The following function returns a trueif the string passed to it consists of onlydigits You can call this function prior to converting into a type of integer,assuming that a sequence of nothing but digits is probably a legal number.You would need to include the decimal point for floating point variables andinclude a leading minus sign for negative numbers — but hey, you get the idea.Here’s the function:
// IsAllDigits - return a true if all the characters // in the string are digits
public static bool IsAllDigits(string sRaw) {
// first get rid of any benign characters // at either end; if there’s nothing left // then we don’t have a number
string s = sRaw.Trim(); // ignore whitespace on either side
Trang 11if (s.Length == 0) {
return false;
} // loop through the string for(int index = 0; index < s.Length; index++) {
// a non-digit indicates that the string // probably is not a number
if (Char.IsDigit(s[index]) == false) {
return false;
} } // no non-digits found; it’s probably OK return true;
// IsAllDigits - demonstrate the IsAllDigits method using System;
namespace IsAllDigits {
class Program {
public static void Main(string[] args) {
// input a string from the keyboard Console.WriteLine(“Enter an integer number”);
string s = Console.ReadLine();
// first check to see if this could be a number
if (!IsAllDigits(s)) {
Console.WriteLine(“Hey! That isn’t a number”);
} else { // convert the string into an integer int n = Int32.Parse(s);
// now write out the number times 2
Trang 12Console.WriteLine(“2 * {0} = {1}”, n, 2 * n);
} // wait for user to acknowledge the results Console.WriteLine(“Press Enter to terminate ”);
Console.Read();
} } }
The program reads a line of input from the console keyboard If IsAllDigits()
returns a false, the program alerts the user If not, the program converts thestring into a number using an alternative to Convert.ToInt32()— the Int32.Parse(s)call Finally, the program outputs both the number and two timesthe number (the latter to prove that the program did, in fact, convert thestring as advertised)
The output from a sample run of the program appears as follows:
Enter an integer number 1A3
Hey! That isn’t a number Press Enter to terminate
You could let Converttry to convert garbage and handle any exception itmay decide to throw However, a better-than-even chance exists that it won’tthrow an exception, but just return incorrect results — for example, returning
1 when presented with 1A3 It’s best to validate input data yourself
Handling a series of numbersOften, a program receives a series of numbers in a single line from the key-board Using the String.Split()method, you can easily break the stringinto a number of substrings, one for each number, and parse them separately.The Split()function chops up a single string into an array of smaller stringsusing some delimiter For example, if you tell Split()to divide a string using acomma (,) as the delimiter, “1,2,3”becomes three strings, “1”, “2”, and “3”.The following program uses the Split()method to input a sequence of num-bers to be summed:
// ParseSequenceWithSplit - input a series of numbers // separated by commas, parse them into // integers, and output the sum namespace ParseSequenceWithSplit
{ using System;
class Program