Fundamental Types - Strings, Arrays, and Enums

42 362 0
Fundamental Types - Strings, Arrays, and Enums

Đang tải... (xem toàn văn)

Tài liệu hạn chế xem trước, để xem đầy đủ mời bạn chọn Tải xuống

Thông tin tài liệu

75 ■ ■ ■ CHAPTER 5 Fundamental Types: Strings, Arrays, and Enums I n this chapter, you’ll learn about some special types in the C++/CLI type system. I have been using the term primitive type to refer to the built-in integral and floating-point types. Other types, such as those discussed in this chapter, are built upon these primitive types and are fundamental to any program. Each of these types is a .NET version of a classic C++ concept, and each of these has special language support in addition to being a bona fide .NET Frame- work object type. The chapter will go into some detail not just on the syntax and mechanics of the types themselves, but also some of the commonly used .NET Framework library function- ality related to these types. My primary aim in this book is to focus on the C++/CLI language itself, not the .NET Framework. However, input and output is so fundamental to any language that it’s worth discussing on its own, and what better place to discuss it than in the context of strings? Input and output of text are necessary for almost any application, not just an old-style console appli- cation. You might need to output text to a string for display in a user interface or for a formatted file. Output usually involves manipulating strings, so this chapter first looks in depth at the String type. The String type is the one that actually provides much of the formatting capability needed in output, whether it’s to the console or a web application or a graphical user interface. Strings The String type is a reference type that consists of a sequence of Unicode characters representing text. The class has many useful instance methods and many static methods that support copying and manipulation of strings. The String class represents an immutable sequence of characters; methods that manipulate strings do not modify them in-place, they create new, modified versions of the strings. Even those methods that suggest that they modify the string (such as Insert, Remove, Replace, etc.) create new strings. If you need a string that is modifiable in-place, use the StringBuilder class. Let’s start with a few basics. To create a simple string, write code like the following: String^ str = gcnew String("Text"); String^ str1 = "Text"; Hogenson_705-2C05.fm Page 75 Friday, October 13, 2006 2:39 PM 76 CHAPTER 5 ■ FUNDAMENTAL TYPES: STRINGS, ARRAYS, AND ENUMS In the first statement, we explicitly spell out the String constructor with a string literal argument. In the second statement, the right side is a string literal. Historically, in Visual C++, if you use the prefix L in front of the string, the string literal is interpreted as Unicode. C++/CLI interprets string literals as narrow 1-byte characters or wide 2-byte Unicode characters depending on the context. If a string literal is immediately assigned to a String, it is interpreted as a wide character string literal even without the L prefix. In any event, the String class always refers to a Unicode string, and an automatic conversion from a string literal is defined in C++/CLI, so when the string literal is assigned to a string handle, the result is a handle to a Unicode string. Because of the context-dependent nature of string literals, it is sometimes said that the type of a string literal is inexpressible in the language. In practical terms, it simply means that you can use string literals without a lot of fussing about the types involved. To concatenate two strings, use the static method String::Concat, as follows: String^ str = String::Concat(str1, str2); What this does is create a new String object, str, that is the concatenation of str1 and str2. The str1 and str2 objects themselves are left unmodified. To get at a single character in a string, use the Chars indexed property, as follows: char c = str1->Chars[5]; You’ll read more about indexed properties in Chapter 7; the indexed property Chars allows array-indexing syntax to be used on the property. To copy a string, you can either make another reference to the same string or copy the string. Depending on your application, one or the other might make the most sense. The assignment operator creates another reference to the same string. This is what is meant by a shallow copy. The Copy member function creates a new string, which is known as a deep copy. Since String objects cannot be modified, multiple references to the string will retain the correct value; thus it is usually not necessary to copy the string. However, to compare strings, you must be aware of whether you’re comparing the refer- ence (testing for reference equality) or the characters of the strings themselves. The equality operator (==) is equivalent to the Equals method, and both test for equality of a string’s value. The example in Listing 5-1 demonstrates this. Listing 5-1. Comparing Strings // string_equality.cpp using namespace System; int main() { String^ str1 = "1"; String^ str2 = "1"; String^ str3 = str1; Hogenson_705-2C05.fm Page 76 Friday, October 13, 2006 2:39 PM CHAPTER 5 ■ FUNDAMENTAL TYPES: STRINGS, ARRAYS, AND ENUMS 77 // All of the following tests result in True, since // the == operator is equivalent to the Equals method. if (str1 == str2) { Console::WriteLine(" str1 == str2" ); } if (str1 == str3) { Console::WriteLine(" str1 == str3" ); } if (str1->Equals(str2)) { Console::WriteLine(" str1 Equals str2" ); } if (str1->Equals(str3)) { Console::WriteLine(" str1 Equals str3"); } // ReferenceEquals compares the handles, not the actual // string. The results are implementation dependent, // since if the compiler creates a single-string representation // for both string literals, as is the case here, this will resolve // true. if (String::ReferenceEquals(str1, str2)) { Console::WriteLine(" str1 ReferenceEquals str2"); } if (String::ReferenceEquals(str1, str3)) { Console::WriteLine(" str1 ReferenceEquals str3"); } } The output of Listing 5-1 is as follows: str1 == str2 str1 == str3 str1 Equals str2 str1 Equals str3 str1 ReferenceEquals str2 str1 ReferenceEquals str3 To get the string as an array of characters, you can convert it to a character array using the ToCharArray method, as shown in Listing 5-2. Unlike the Chars property, this creates a new array of System::Char that contains a copy of each character in the string. System::Char is also known as wchar_t, the Unicode character type. Hogenson_705-2C05.fm Page 77 Friday, October 13, 2006 2:39 PM 78 CHAPTER 5 ■ FUNDAMENTAL TYPES: STRINGS, ARRAYS, AND ENUMS Listing 5-2. Converting a String to a Character Array // string_tochararray.cpp using namespace System; int main() { String^ str = "A quick sly fox jumped over the lazy brown dog."; array<Char>^ character_array = str->ToCharArray(); // Print the original string. Console::WriteLine( str); // Modify characters in the character array. for (int i = 0; i < character_array->Length; i++) { if ( character_array[i] >= L'a' && character_array[i] <= 'z') { character_array[i] -= (L'a' - L'A'); } } // Convert back to a String using the String constructor // that takes a Unicode character array. str = gcnew String(character_array); // Print the modified string: // A QUICK SLY FOX JUMPED OVER THE LAZY BROWN DOG. Console::WriteLine( str); } The output of Listing 5-2 is shown here: A quick sly fox jumped over the lazy brown dog. A QUICK SLY FOX JUMPED OVER THE LAZY BROWN DOG. Or, if you need to iterate over characters in a string, use the for each statement, as in Listing 5-3. Listing 5-3. Looping Through a String // string_foreach.cpp using namespace System; Hogenson_705-2C05.fm Page 78 Friday, October 13, 2006 2:39 PM CHAPTER 5 ■ FUNDAMENTAL TYPES: STRINGS, ARRAYS, AND ENUMS 79 int main() { String^ str1 = "Ode on a Grecian Urn"; for each (Char ch in str1) { Console::Write(ch); } Console::WriteLine(); } Here’s the output of Listing 5-3: Ode on a Grecian Urn This code works because first, the String class implements the interface IEnumerable, and second, the GetEnumerator function returns a CharEnumerator, a class that implements IEnumerator. IEnumerator includes a property, Current, which in the case of CharEnumerator, returns a Char (which, as mentioned earlier, is the same as wchar_t). String Operators C++/CLI supports, for convenience, the use of the + operator on strings, string literals, and other entities that can be converted to strings (which includes any managed type, since Object defined the ToString method that other objects inherit). The result is the concatenation of the strings. In this way you can build up an output string using concatenation, rather than using the format string. This is not generally a good idea for applications that must be localized into other languages, since the different word order of different languages may mean that the order of concatenation is language dependent. Listing 5-4 shows the use of the string concatenation operator. Listing 5-4. Concatenating Strings // string_operator_plus.cpp using namespace System; int main() { String ^hrs = "Hours", ^mins = "Minutes"; wchar_t separator = ':'; int minutes = 56, hours = 1; Console::WriteLine( hrs + separator + " " + hours + "\n" + mins + separator + " " + minutes); } Hogenson_705-2C05.fm Page 79 Friday, October 13, 2006 2:39 PM 80 CHAPTER 5 ■ FUNDAMENTAL TYPES: STRINGS, ARRAYS, AND ENUMS The output of Listing 5-4 is as follows: Hours: 1 Minutes: 56 The addition operator works from left to right, so as long as the first operand is a string, each operand in the series will be converted to a string, even if some part of the expression could also be interpreted as another type of addition (such as adding integers). The ToString function is used to convert types to strings. The string concatenation operator works with all managed types, since all managed types inherit the ToString operator from System::Object. Strings that are editable (also called mutable) should be instances of StringBuilder rather than String. You’ll learn more about StringBuilder strings later in this chapter. Comparing Strings Strings implement IComparable, so they support the CompareTo method to compare to another string. You can also use the static method, Compare, to compare two strings. The version of the Compare static method that takes only two strings as parameters and the CompareTo method use the same comparison algorithm, but the Compare static method is overloaded and has several variations that allow the comparison to be customized. The CompareTo method, for any object that implements IComparable, returns a value representing one of three possibilities. A negative return value indicates that the first object is less than the second. A zero return value indicates that the two objects are equal. A positive return value indicates that the first object is greater than the second. For the CompareTo method, the first object is the object whose instance method is being called; for the static method, the first object is the first argument. Listing 5-5 shows the basic use of string comparison. Listing 5-5. Comparing Strings with CompareTo // string_compare.cpp using namespace System; int main() { String^ str1 = "cat"; String^ str2 = "cab"; if (str1->CompareTo( str2 ) < 0) { Console::WriteLine(str1 + " is less than " + str2); } // For variety, use the static method. else if ( String::Compare(str1, str2) > 0 ) { Console::WriteLine("{0} is less than {1}", str2, str1); } Hogenson_705-2C05.fm Page 80 Friday, October 13, 2006 2:39 PM CHAPTER 5 ■ FUNDAMENTAL TYPES: STRINGS, ARRAYS, AND ENUMS 81 else if ( str1->CompareTo( str2 ) == 0) { Console::WriteLine("The strings are both equal, with value {0}.", str1); } } Here is the output of Listing 5-5: cab is less than cat Implementing the IComparable interface allows strings to be used in all sorts of container classes where comparison is a requirement. For example, in Chapter 11, you’ll see how to define a generic collection class that has a constraint indicating that any class used in the generic collection must implement IComparable. This allows the author of the generic class to assume certain functionality, such as the existence of the CompareTo method. The CompareTo method alone isn’t rich enough to support all the factors that might be rele- vant in comparing strings in real-world code. Sometimes comparison must be case sensitive, other times comparison must be case insensitive. Additionally, comparison in some applica- tions must be sensitive to culture, since alphabets and alphabetical order are dependent on locale. The CompareTo method also includes overloads that support comparison of substrings. There’s also a CompareOrdinal method that is useful if the strings represent numbers and you want a comparison of the number. Formatting Strings The Format methods format a string for output. The .NET Framework formatting support is very rich, supporting a highly customizable output format and providing an extensible frame- work for defining your own custom formats as well. The same formatting rules are used for the Console class’s WriteLine method for output to the console. The string used to specify the desired formatting and that acts as a template for the output is called the format string. The format string contains placeholders that are numbered starting with zero and surrounded by curly braces, as in the following string: Console::WriteLine("The population of {0} is {1}.", "Pleasantville", 500); This code substitutes Pleasantville for the {0} and 500 for the {1}. The type of the argu- ment need not be supplied, as the language contains enough type information without any further specification. The number in curly braces is referred to as the index. It is followed, optionally, by a comma and number specifying the minimum width of the field. The sign of the number specifies the justification (positive for right-justification, negative for left-justification). One can also append a colon and a formatting string that is used to customize the output format. The available formatting strings are dependent on the type. A variety of formatting codes exists for formatting numeric output, as well as date, time, and currency output, which is dependent on the locale. The following sections provide detailed examples. Hogenson_705-2C05.fm Page 81 Friday, October 13, 2006 2:39 PM 82 CHAPTER 5 ■ FUNDAMENTAL TYPES: STRINGS, ARRAYS, AND ENUMS The Width Field (or Alignment Specifier) Listing 5-6 provides some examples of formatting using the width field, including a negative width indicating left justification, and a currency formatting string—the c2 following the colon in the Price column, which is ignored when used with a string. Listing 5-6. Formatting Strings Using the Width Field // string_alignment_specifier.cpp using namespace System; int main() { // The format string is interpreted as follows: // { 0, -30 } 30 characters in width, left-justified. // { 1, 10 } 10 characters in width, right-justified. // { 2, 10:c2 } 10 characters in width, currency with 2 decimal places. String^ format = "{0,-30}{1,10}{2,10:c2}"; String^ header = String::Format(format, "Item", "Quantity", "Price"); String^ str1 = str1->Format(format, "Matches, Strike Anywhere", 10, 0.99); String^ str2 = str2->Format(format, "Gloves", 1, 12.50); String^ str3 = str3->Format(format, "Iodine", 1, 4.99); Console::WriteLine(header); Console::WriteLine(str1 + "\n" + str2 + "\n" + str3); } The output of Listing 5-6 on U.S. English systems is as follows: Item Quantity Price Matches, Strike Anywhere 10 $0.99 Gloves 1 $12.50 Iodine 1 $4.99 Numeric String Formatting Formatting in C runtime functions such as printf involves the use of formatting characters for various data types and, in particular, certain formatting characters for decimal or hexadecimal output, exponential format, and so on. The usual numeric formatting characters from C are supported, as well as additional formats for currency, and a special round-trip format specifi- cally to ensure accurate results when reading the data back in using the Read or ReadLine methods. The code in Listing 5-7 shows the typical use of these formats. The formatting specifier follows the colon after the index (and optional alignment specifier specifying the width of the field) in the format string. In the following example, the alignment specifier is not used, and the index is always zero since we only have one variable to format. Hogenson_705-2C05.fm Page 82 Friday, October 13, 2006 2:39 PM CHAPTER 5 ■ FUNDAMENTAL TYPES: STRINGS, ARRAYS, AND ENUMS 83 Listing 5-7. Formatting Numeric Strings // string_numerical_formatting.cpp using namespace System; int main() { String^ str; int i = -73000; double dbl = 1005.01; // Formats for floating-point types: str = String::Format("Currency format: {0:c2}", dbl); Console::WriteLine(str); str = String::Format("Scientific format: {0:e6}", dbl); Console::WriteLine(str); str = String::Format("Fixed-point format: {0:f6}", dbl); Console::WriteLine(str); str = String::Format("General format: {0:g6}", dbl); Console::WriteLine(str); str = String::Format("Number format: {0:n6}", dbl); Console::WriteLine(str); str = String::Format("Percent format: {0:p6}", dbl); Console::WriteLine(str); str = String::Format("Round-trip format: {0:r6}", dbl); Console::WriteLine(str); // Formats for integral types: str = String::Format("Decimal format: {0:d6}", i); Console::WriteLine(str); str = String::Format("General format: {0:g6}", i); Console::WriteLine(str); str = String::Format("Number format: {0:n0}", i); Console::WriteLine(str); str = String::Format("Hexadecimal format: {0:x8}", i); Console::WriteLine(str); } Hogenson_705-2C05.fm Page 83 Friday, October 13, 2006 2:39 PM 84 CHAPTER 5 ■ FUNDAMENTAL TYPES: STRINGS, ARRAYS, AND ENUMS Here is the output of Listing 5-7: Currency format: $1,005.01 Scientific format: 1.005010e+003 Fixed-point format: 1005.010000 General format: 1005.01 Number format: 1,005.010000 Percent format: 100,501.000000 % Round-trip format: 1005.01 Decimal format: -073000 General format: -73000 Number format: -73,000 Hexadecimal format: fffee2d8 StringBuilder For manipulation and editing of strings in-place, you need to use StringBuilder rather than String. StringBuilder contains methods for appending, inserting, removing, and replacing elements of a string (see Listing 5-8). StringBuilder maintains an internal buffer with a given capacity and expands this capacity as the size of the string increases. Listing 5-8. Using StringBuilder // stringbuilder.cpp using namespace System; using namespace System::Text; int main() { // Construct a StringBuilder string with initial contents // "C" and initial capacity 30. StringBuilder^ sb = gcnew StringBuilder("C", 30); sb->Append(gcnew array<Char>{'+','+'}); sb->Append("/CLI."); sb->Insert(0, "I love "); sb->Replace(".","!"); Console::WriteLine( sb->ToString() ); } The output of Listing 5-8 is as follows: Hogenson_705-2C05.fm Page 84 Friday, October 13, 2006 2:39 PM [...]... Console::WriteLine('A'); } Hogenson_70 5-2 C05.fm Page 87 Friday, October 13, 2006 2:39 PM CHAPTER 5 ■ FUNDAMENTAL TYPES: STRINGS, ARRAYS, AND ENUMS The output of Listing 5-1 0 is as follows: abc Fourscore and 7 years ago 7 1.05 65 Out, Error, and In The Console class exposes the Out, Error and In properties as abstractions for the standard filestreams stdout, stderr, and stdin Out, Error, and In are represented as... jambo."); sw->WriteLine("The scent of a market flower,"); sw->WriteLine("Open wide to all of it and" ); sw->WriteLine("Welcome back your power"); sw->WriteLine(); sw->WriteLine("Jambo this and jambo that,"); sw->WriteLine("Walking with bare feet."); sw->WriteLine("No parking allowed when down under,"); sw->WriteLine("Keep it to the street."); sw->WriteLine(); sw->WriteLine("Dead people rising,"); sw->WriteLine("Walking... strings in-place, and the Console class for input and output to the console or command-line window, including a discussion of the In, Out, and Error representations of stdin, stdout, and stderr I covered the Write and WriteLine methods, the Read and Readline methods, and file I/O using the StreamWriter and StreamReader classes, and corresponding functionality for string I/O in the StringWriter and StringReader... which has static methods for creating or opening files (see Listing 5-1 2) 87 Hogenson_70 5-2 C05.fm Page 88 Friday, October 13, 2006 2:39 PM 88 CHAPTER 5 ■ FUNDAMENTAL TYPES: STRINGS, ARRAYS, AND ENUMS Listing 5-1 2 Using StreamWriter StreamWriter^ sw = gcnew StreamWriter("textfile.txt"); sw->WriteLine("Can code be poetry?"); sw->Flush(); sw->Close(); // The File class's CreateText static method is used to... System::String, as in Listing 5-1 6 Note that as of Visual C++ 2005, it is recommended that Hogenson_70 5-2 C05.fm Page 91 Friday, October 13, 2006 2:39 PM CHAPTER 5 ■ FUNDAMENTAL TYPES: STRINGS, ARRAYS, AND ENUMS you use the more secure variants of the standard CRT functions While these are not yet part of the ANSI standard, they have been proposed as extensions to the standard Listing 5-1 6 Using printf // cli_printf.cpp... sw->WriteLine(s); sw->Write("Make a wish, {0}, {0}.", jambo); sw->WriteLine(); 89 Hogenson_70 5-2 C05.fm Page 90 Friday, October 13, 2006 2:39 PM 90 CHAPTER 5 ■ FUNDAMENTAL TYPES: STRINGS, ARRAYS, AND ENUMS s = "Feel it, grab it, grope it.\n"; String::Concat(s, "Follow every curve.\n"); String::Concat(s, "Can you wait to find it?\n"); String::Concat(s, "Do you have the nerve?"); sw->WriteLine(s); sw->WriteLine("A... 5-1 8 illustrates various initializers, showing the native and managed equivalents side by side 93 Hogenson_70 5-2 C05.fm Page 94 Friday, October 13, 2006 2:39 PM 94 CHAPTER 5 ■ FUNDAMENTAL TYPES: STRINGS, ARRAYS, AND ENUMS Listing 5-1 8 Initializing Arrays // arrays_initializing.cpp int main() { // Declare, create, and initialize a 1D native array int native_array[2] = { 10, 20 }; // Declare, create, and. .. create a managed array with native aggregate types such as native arrays, classes, and structures Table 5-1 outlines differences between native arrays and managed arrays Hogenson_70 5-2 C05.fm Page 101 Friday, October 13, 2006 2:39 PM CHAPTER 5 ■ FUNDAMENTAL TYPES: STRINGS, ARRAYS, AND ENUMS Table 5-1 Differences Between Native and Managed Arrays Difference Native Array Managed Array Underlying representation?... // arrays of int bool ReallyEquals(array^ a, array^ b) { if (a->Length != b->Length) return false; // Element-by-element comparison for (int i = 0; i < a->Length; i++) { if (a[i] != b[i]) return false; } Hogenson_70 5-2 C05.fm Page 107 Friday, October 13, 2006 2:39 PM CHAPTER 5 ■ FUNDAMENTAL TYPES: STRINGS, ARRAYS, AND ENUMS return true; } int main() { array^ ai1 = gcnew array { 1,... Listing 5-2 7 99 Hogenson_70 5-2 C05.fm Page 100 Friday, October 13, 2006 2:39 PM 100 CHAPTER 5 ■ FUNDAMENTAL TYPES: STRINGS, ARRAYS, AND ENUMS Listing 5-2 7 Going Past the End of an Array // array_exception.cpp using namespace System; int main() { int i; array^ array1 = { 0, 1, 2}; try { i = array1[3]; } catch(IndexOutOfRangeException^ e) { Console::WriteLine( "{0}, {1}" , e->ToString(), e->Message); . Listing 5-1 2). Hogenson_70 5-2 C05.fm Page 87 Friday, October 13, 2006 2:39 PM 88 CHAPTER 5 ■ FUNDAMENTAL TYPES: STRINGS, ARRAYS, AND ENUMS Listing 5-1 2. Using. type. Hogenson_70 5-2 C05.fm Page 77 Friday, October 13, 2006 2:39 PM 78 CHAPTER 5 ■ FUNDAMENTAL TYPES: STRINGS, ARRAYS, AND ENUMS Listing 5-2 . Converting

Ngày đăng: 05/10/2013, 07:20

Tài liệu cùng người dùng

Tài liệu liên quan