diff --git a/docs/csharp/how-to/compare-strings.md b/docs/csharp/how-to/compare-strings.md index c86f6252600ab..7aa1f0f58a125 100644 --- a/docs/csharp/how-to/compare-strings.md +++ b/docs/csharp/how-to/compare-strings.md @@ -1,7 +1,7 @@ --- title: "How to compare strings" description: Learn how to compare and order string values, with or without case, with or without culture specific ordering. -ms.date: 03/15/2024 +ms.date: 02/18/2025 helpviewer_keywords: - "strings [C#], comparison" - "comparing strings [C#]" @@ -11,7 +11,7 @@ helpviewer_keywords: You compare strings to answer one of two questions: "Are these two strings equal?" or "In what order should these strings be placed when sorting them?" -Those two questions are complicated by factors that affect string comparisons: +The following factors complicate these two questions: - You can choose an ordinal or linguistic comparison. - You can choose if case matters. @@ -38,18 +38,20 @@ By default, the most common operations: - - and , that is, [equality operators `==` and `!=`](../language-reference/operators/equality-operators.md#string-equality), respectively perform a case-sensitive, ordinal comparison. has an overload where a argument can be provided to alter its sorting rules. The following example demonstrates that: -:::code language="csharp" interactive="try-dotnet-method" source="../../../samples/snippets/csharp/how-to/strings/CompareStrings.cs" id="Snippet1"::: +:::code language="csharp" interactive="try-dotnet-method" source="./snippets/strings/CompareStrings.cs" id="Snippet1"::: The default ordinal comparison doesn't take linguistic rules into account when comparing strings. It compares the binary value of each object in two strings. As a result, the default ordinal comparison is also case-sensitive. The test for equality with and the `==` and `!=` operators differs from string comparison using the and methods. They all perform a case-sensitive comparison. However, while the tests for equality perform an ordinal comparison, the `CompareTo` and `Compare` methods perform a culture-aware linguistic comparison using the current culture. Make the intent of your code clear by calling an overload that explicitly specifies the type of comparison to perform. +You can use the [`is`](../language-reference/operators/is.md) operator and a [constant pattern](../language-reference/operators/patterns.md#constant-pattern) as an alternative to `==` when the right operand is a constant. + ## Case-insensitive ordinal comparisons The method enables you to specify a value of for a case-insensitive ordinal comparison. There's also a static method that performs a case-insensitive ordinal comparison if you specify a value of for the argument. These comparisons are shown in the following code: -:::code language="csharp" interactive="try-dotnet-method" source="../../../samples/snippets/csharp/how-to/strings/CompareStrings.cs" id="Snippet2"::: +:::code language="csharp" interactive="try-dotnet-method" source="./snippets/strings/CompareStrings.cs" id="Snippet2"::: These methods use the casing conventions of the [invariant culture](xref:System.Globalization.CultureInfo.InvariantCulture) when performing a case-insensitive ordinal comparison. @@ -57,15 +59,15 @@ These methods use the casing conventions of the [invariant culture](xref:System. Many string comparison methods (such as ) use linguistic rules for the _current culture_ by default to order their inputs. This linguistic comparison is sometimes referred to as "word sort order." When you perform a linguistic comparison, some nonalphanumeric Unicode characters might have special weights assigned. For example, the hyphen "-" might have a small weight assigned to it so that "co-op" and "coop" appear next to each other in sort order. Some nonprinting control characters might be ignored. In addition, some Unicode characters might be equivalent to a sequence of instances. The following example uses the phrase "They dance in the street." in German with the "ss" (U+0073 U+0073) in one string and 'ß' (U+00DF) in another. Linguistically (in Windows), "ss" is equal to the German Esszet: 'ß' character in both the "en-US" and "de-DE" cultures. -:::code language="csharp" interactive="try-dotnet-method" source="../../../samples/snippets/csharp/how-to/strings/CompareStrings.cs" id="Snippet3"::: +:::code language="csharp" interactive="try-dotnet-method" source="./snippets/strings/CompareStrings.cs" id="Snippet3"::: -On Windows, prior to .NET 5, the sort order of "cop", "coop", and "co-op" changes when you change from a linguistic comparison to an ordinal comparison. The two German sentences also compare differently using the different comparison types. Prior to .NET 5, the .NET globalization APIs used [National Language Support (NLS)](/windows/win32/intl/national-language-support) libraries. In .NET 5 and later versions, the .NET globalization APIs use [International Components for Unicode (ICU)](https://icu.unicode.org/) libraries, which unifies .NET's globalization behavior across all supported operating systems. +On Windows, before .NET 5, the sort order of "cop", "coop", and "co-op" changes when you change from a linguistic comparison to an ordinal comparison. The two German sentences also compare differently using the different comparison types. Before .NET 5, the .NET globalization APIs used [National Language Support (NLS)](/windows/win32/intl/national-language-support) libraries. In .NET 5 and later versions, the .NET globalization APIs use [International Components for Unicode (ICU)](https://icu.unicode.org/) libraries, which unify .NET's globalization behavior across all supported operating systems. ## Comparisons using specific cultures The following example stores objects for the en-US and de-DE cultures. The comparisons are performed using a object to ensure a culture-specific comparison. The culture used affects linguistic comparisons. The following example shows the results of comparing the two German sentences using the "en-US" culture and the "de-DE" culture: -:::code language="csharp" interactive="try-dotnet-method" source="../../../samples/snippets/csharp/how-to/strings/CompareStrings.cs" id="Snippet4"::: +:::code language="csharp" interactive="try-dotnet-method" source="./snippets/strings/CompareStrings.cs" id="Snippet4"::: Culture-sensitive comparisons are typically used to compare and sort strings input by users with other strings input by users. The characters and sorting conventions of these strings might vary depending on the locale of the user's computer. Even strings that contain identical characters might sort differently depending on the culture of the current thread. @@ -75,21 +77,21 @@ The following examples show how to sort and search for strings in an array using The following example shows how to sort an array of strings using the current culture: -:::code language="csharp" interactive="try-dotnet-method" source="../../../samples/snippets/csharp/how-to/strings/CompareStrings.cs" id="Snippet5"::: +:::code language="csharp" interactive="try-dotnet-method" source="./snippets/strings/CompareStrings.cs" id="Snippet5"::: Once the array is sorted, you can search for entries using a binary search. A binary search starts in the middle of the collection to determine which half of the collection would contain the sought string. Each subsequent comparison subdivides the remaining part of the collection in half. The array is sorted using the . The local function `ShowWhere` displays information about where the string was found. If the string wasn't found, the returned value indicates where it would be if it were found. -:::code language="csharp" interactive="try-dotnet-method" source="../../../samples/snippets/csharp/how-to/strings/CompareStrings.cs" id="Snippet6"::: +:::code language="csharp" interactive="try-dotnet-method" source="./snippets/strings/CompareStrings.cs" id="Snippet6"::: ## Ordinal sorting and searching in collections The following code uses the collection class to store strings. The strings are sorted using the method. This method needs a delegate that compares and orders two strings. The method provides that comparison function. Run the sample and observe the order. This sort operation uses an ordinal case-sensitive sort. You would use the static methods to specify different comparison rules. -:::code language="csharp" interactive="try-dotnet-method" source="../../../samples/snippets/csharp/how-to/strings/CompareStrings.cs" id="Snippet7"::: +:::code language="csharp" interactive="try-dotnet-method" source="./snippets/strings/CompareStrings.cs" id="Snippet7"::: Once sorted, the list of strings can be searched using a binary search. The following sample shows how to search the sorted list using the same comparison function. The local function `ShowWhere` shows where the sought text is or would be: -:::code language="csharp" interactive="try-dotnet-method" source="../../../samples/snippets/csharp/how-to/strings/CompareStrings.cs" id="Snippet8"::: +:::code language="csharp" interactive="try-dotnet-method" source="./snippets/strings/CompareStrings.cs" id="Snippet8"::: Always make sure to use the same type of comparison for sorting and searching. Using different comparison types for sorting and searching produces unexpected results. diff --git a/docs/csharp/how-to/concatenate-multiple-strings.md b/docs/csharp/how-to/concatenate-multiple-strings.md index ba73d17d4c47d..e5dd559e2324e 100644 --- a/docs/csharp/how-to/concatenate-multiple-strings.md +++ b/docs/csharp/how-to/concatenate-multiple-strings.md @@ -1,7 +1,7 @@ --- title: "How to concatenate multiple strings" description: There are multiple ways to concatenate strings in C#. Learn the options and the reasons behind different choices. -ms.date: 1/31/2025 +ms.date: 02/18/2025 helpviewer_keywords: - "joining strings [C#]" - "concatenating strings [C#]" @@ -22,19 +22,19 @@ ms.custom: copilot-scenario-highlight The following example splits a long string literal into smaller strings to improve readability in the source code. The code concatenates the smaller strings to create the long string literal. The parts are concatenated into a single string at compile time. There's no run-time performance cost regardless of the number of strings involved. -:::code language="csharp" interactive="try-dotnet-method" source="../../../samples/snippets/csharp/how-to/strings/Concatenate.cs" id="Snippet1"::: +:::code language="csharp" interactive="try-dotnet-method" source="./snippets/strings/Concatenate.cs" id="Snippet1"::: ## `+` and `+=` operators To concatenate string variables, you can use the `+` or `+=` operators, [string interpolation](../language-reference/tokens/interpolated.md) or the , , or methods. The `+` operator is easy to use and makes for intuitive code. Even if you use several `+` operators in one statement, the string content is copied only once. The following code shows examples of using the `+` and `+=` operators to concatenate strings: -:::code language="csharp" interactive="try-dotnet-method" source="../../../samples/snippets/csharp/how-to/strings/Concatenate.cs" id="Snippet2"::: +:::code language="csharp" interactive="try-dotnet-method" source="./snippets/strings/Concatenate.cs" id="Snippet2"::: ## String interpolation In some expressions, it's easier to concatenate strings using string interpolation, as the following code shows: -:::code language="csharp" interactive="try-dotnet-method" source="../../../samples/snippets/csharp/how-to/strings/Concatenate.cs" id="Snippet3"::: +:::code language="csharp" interactive="try-dotnet-method" source="./snippets/strings/Concatenate.cs" id="Snippet3"::: > [!NOTE] > In string concatenation operations, the C# compiler treats a null string the same as an empty string. @@ -43,13 +43,13 @@ You can use string interpolation to initialize a constant string when all the ex ## `String.Format` -Another method to concatenate strings is . This method works well when you're building a string from a small number of component strings. +Another method to concatenate strings is . This method works well when you're building a string from a few component strings. ## `StringBuilder` -In other cases, you might be combining strings in a loop where you don't know how many source strings you're combining, and the actual number of source strings can be large. The class was designed for these scenarios. The following code uses the method of the class to concatenate strings. +In other cases, you might be combining strings in a loop where the actual number of source strings can be large. The class was designed for these scenarios. The following code uses the method of the class to concatenate strings. -:::code language="csharp" interactive="try-dotnet-method" source="../../../samples/snippets/csharp/how-to/strings/Concatenate.cs" id="Snippet4"::: +:::code language="csharp" interactive="try-dotnet-method" source="./snippets/strings/Concatenate.cs" id="Snippet4"::: You can read more about the [reasons to choose string concatenation or the `StringBuilder` class](/dotnet/api/system.text.stringbuilder#the-string-and-stringbuilder-types). @@ -57,7 +57,7 @@ You can read more about the [reasons to choose string concatenation or the `Stri Another option to join strings from a collection is to use method. Use method if a delimiter should separate source strings. The following code combines an array of words using both methods: -:::code language="csharp" interactive="try-dotnet-method" source="../../../samples/snippets/csharp/how-to/strings/Concatenate.cs" id="Snippet5"::: +:::code language="csharp" interactive="try-dotnet-method" source="./snippets/strings/Concatenate.cs" id="Snippet5"::: ## LINQ and `Enumerable.Aggregate` @@ -67,7 +67,7 @@ the source strings using a lambda expression. The lambda expression does the work to add each string to the existing accumulation. The following example combines an array of words, adding a space between each word in the array: -:::code language="csharp" interactive="try-dotnet-method" source="../../../samples/snippets/csharp/how-to/strings/Concatenate.cs" id="Snippet6"::: +:::code language="csharp" interactive="try-dotnet-method" source="./snippets/strings/Concatenate.cs" id="Snippet6"::: This option can cause more allocations than other methods for concatenating collections, as it creates an intermediate string for each iteration. If optimizing performance is critical, consider the [`StringBuilder`](#stringbuilder) class or the [`String.Concat` or `String.Join`](#stringconcat-or-stringjoin) method to concatenate a collection, instead of `Enumerable.Aggregate`. diff --git a/docs/csharp/how-to/modify-string-contents.md b/docs/csharp/how-to/modify-string-contents.md index a0551a4bdc935..4eeb496c7f4f1 100644 --- a/docs/csharp/how-to/modify-string-contents.md +++ b/docs/csharp/how-to/modify-string-contents.md @@ -1,7 +1,7 @@ --- title: "How to modify string contents" description: Review examples of several techniques to modify existing string contents in C#, which return a new string object. -ms.date: 02/26/2018 +ms.date: 02/18/2025 helpviewer_keywords: - "strings [C#], modifying" --- @@ -17,27 +17,27 @@ There are several techniques demonstrated in this article. You can replace exist The following code creates a new string by replacing existing text with a substitute. -:::code language="csharp" interactive="try-dotnet-method" source="../../../samples/snippets/csharp/how-to/strings/ModifyStrings.cs" id="Snippet1"::: +:::code language="csharp" interactive="try-dotnet-method" source="./snippets/strings/ModifyStrings.cs" id="Snippet1"::: -The preceding code demonstrates this *immutable* property of strings. You can see in the preceding example that the original string, `source`, is not modified. The method creates a new `string` containing the modifications. +The preceding code demonstrates this *immutable* property of strings. You can see in the preceding example that the original string, `source`, isn't modified. The method creates a new `string` containing the modifications. -The method can replace either strings or single characters. In both cases, every occurrence of the sought text is replaced. The following example replaces all ' ' characters with '\_': +The method can replace either strings or single characters. In both cases, every occurrence of the sought text is replaced. The following example replaces all ' ' characters with '\_': -:::code language="csharp" interactive="try-dotnet-method" source="../../../samples/snippets/csharp/how-to/strings/ModifyStrings.cs" id="Snippet2"::: +:::code language="csharp" interactive="try-dotnet-method" source="./snippets/strings/ModifyStrings.cs" id="Snippet2"::: The source string is unchanged, and a new string is returned with the replacement. ## Trim white space -You can use the , , and methods to remove any leading or trailing white space. The following code shows an example of each. The source string does not change; these methods return a new string with the modified contents. +You can use the , , and methods to remove any leading or trailing white space. The following code shows an example of each. The source string doesn't change; these methods return a new string with the modified contents. -:::code language="csharp" interactive="try-dotnet-method" source="../../../samples/snippets/csharp/how-to/strings/ModifyStrings.cs" id="Snippet3"::: +:::code language="csharp" interactive="try-dotnet-method" source="./snippets/strings/ModifyStrings.cs" id="Snippet3"::: ## Remove text -You can remove text from a string using the method. This method removes a number of characters starting at a specific index. The following example shows how to use followed by to remove text from a string: +You can remove text from a string using the method. This method removes the specified number of characters starting at a specific index. The following example shows how to use followed by to remove text from a string: -:::code language="csharp" interactive="try-dotnet-method" source="../../../samples/snippets/csharp/how-to/strings/ModifyStrings.cs" id="Snippet4"::: +:::code language="csharp" interactive="try-dotnet-method" source="./snippets/strings/ModifyStrings.cs" id="Snippet4"::: ## Replace matching patterns @@ -45,7 +45,7 @@ You can use [regular expressions](../../standard/base-types/regular-expressions. Regular expressions are most useful for searching and replacing text that follows a pattern, rather than known text. For more information, see [How to search strings](search-strings.md). The search pattern, "the\s" searches for the word "the" followed by a white-space character. That part of the pattern ensures that it doesn't match "there" in the source string. For more information on regular expression language elements, see [Regular Expression Language - Quick Reference](../../standard/base-types/regular-expression-language-quick-reference.md). -:::code language="csharp" interactive="try-dotnet-method" source="../../../samples/snippets/csharp/how-to/strings/ModifyStrings.cs" id="Snippet5"::: +:::code language="csharp" interactive="try-dotnet-method" source="./snippets/strings/ModifyStrings.cs" id="Snippet5"::: The method returns an immutable string with the contents in the object. @@ -55,15 +55,15 @@ You can produce a character array from a string, modify the contents of the arra The following example shows how to replace a set of characters in a string. First, it uses the method to create an array of characters. It uses the method to find the starting index of the word "fox." The next three characters are replaced with a different word. Finally, a new string is constructed from the updated character array. -:::code language="csharp" interactive="try-dotnet-method" source="../../../samples/snippets/csharp/how-to/strings/ModifyStrings.cs" id="Snippet6"::: +:::code language="csharp" interactive="try-dotnet-method" source="./snippets/strings/ModifyStrings.cs" id="Snippet6"::: ## Programmatically build up string content -Since strings are immutable, the previous examples all create temporary strings or character arrays. In high-performance scenarios, it may be desirable to avoid these heap allocations. .NET Core provides a method that allows you to programmatically fill in the character content of a string via a callback while avoiding the intermediate temporary string allocations. +Since strings are immutable, the previous examples all create temporary strings or character arrays. In high-performance scenarios, it's desirable to avoid these heap allocations. .NET provides a method that allows you to programmatically fill in the character content of a string via a callback while avoiding the intermediate temporary string allocations. -:::code language="csharp" source="../../../samples/snippets/csharp/how-to/strings/ModifyStrings.cs" id="Snippet7"::: +:::code language="csharp" interactive="try-dotnet-method" source="./snippets/strings/ModifyStrings.cs" id="Snippet7"::: -You could modify a string in a fixed block with unsafe code, but it is **strongly** discouraged to modify the string content after a string is created. Doing so will break things in unpredictable ways. For example, if someone interns a string that has the same content as yours, they'll get your copy and won't expect that you are modifying their string. +You could modify a string in a fixed block with unsafe code, but it's **strongly** discouraged to modify the string content after a string is created. Doing so causes unpredictable bugs. For example, if someone interns a string that has the same content as yours, they get your copy and didn't expect that you're modifying their string. ## See also diff --git a/docs/csharp/how-to/parse-strings-using-split.md b/docs/csharp/how-to/parse-strings-using-split.md index fcff09eb616ec..e2bc6c21c4532 100644 --- a/docs/csharp/how-to/parse-strings-using-split.md +++ b/docs/csharp/how-to/parse-strings-using-split.md @@ -1,54 +1,81 @@ --- title: "Divide strings using String.Split" description: The Split method returns an array of strings split from a set of delimiters. It's an easy way to extract substrings from a string. -ms.date: 10/31/2024 +ms.date: 02/18/2025 helpviewer_keywords: - "splitting strings [C#]" - "Split method [C#]" - "strings [C#], splitting" - "parse strings" -ms.assetid: 729c2923-4169-41c6-9c90-ef176c1e2953 -ms.custom: mvc, copilot-scenario-highlight +ms.custom: copilot-scenario-highlight ms.collection: ce-skilling-ai-copilot --- # How to separate strings using String.Split in C\# -The method creates an array of substrings by splitting the input string based on one or more delimiters. This method is often the easiest way to separate a string on word boundaries. It's also used to split strings on other specific characters or strings. +The method creates an array of substrings by splitting the input string based on one or more delimiters. This method is often the easiest way to separate a string on word boundaries. [!INCLUDE[interactive-note](~/includes/csharp-interactive-note.md)] > [!TIP] > You can use AI assistance to [split a string with GitHub Copilot](#use-github-copilot-to-split-a-string). -## String.Split examples +## Split a string into words The following code splits a common phrase into an array of strings for each word. -:::code language="csharp" interactive="try-dotnet-method" source="../../../samples/snippets/csharp/how-to/strings/ParseStringsUsingSplit.cs" id="Snippet1"::: +:::code language="csharp" interactive="try-dotnet-method" source="./snippets/strings/ParseStringsUsingSplit.cs" id="Snippet1"::: Every instance of a separator character produces a value in the returned array. Since arrays in C# are zero-indexed, each string in the array is indexed from 0 to the value returned by the property minus 1: -:::code language="csharp" interactive="try-dotnet-method" source="../../../samples/snippets/csharp/how-to/strings/ParseStringsUsingSplit.cs" id="Snippet1.5"::: +:::code language="csharp" interactive="try-dotnet-method" source="./snippets/strings/ParseStringsUsingSplit.cs" id="Snippet1.5"::: -Consecutive separator characters produce the empty string as a value in the returned array. You can see how an empty string is created in the following example, which uses the space character as a separator. +The has many overloads. These overloads customize the behavior for splitting strings: -:::code language="csharp" interactive="try-dotnet-method" source="../../../samples/snippets/csharp/how-to/strings/ParseStringsUsingSplit.cs" id="Snippet2"::: +- You can specify separators as `char` values or `string` values. +- You can specify one separator or multiple separators. If you specify multiple separators, they must all be the same type (either `char` or `string`). +- You can specify the maximum number of substrings to return. +- You can specify if repeated separator characters are ignored, or produce empty substrings in the return value. +- You can specify if leading and trailing whitespace is removed from the returned substrings. -This behavior makes it easier for formats like comma-separated values (CSV) files representing tabular data. Consecutive commas represent a blank column. +The remaining examples use different overloads to show each of these behaviors. -You can pass an optional parameter to exclude any empty strings in the returned array. For more complicated processing of the returned collection, you can use [LINQ](/dotnet/csharp/linq/) to manipulate the result sequence. +## Specify multiple separators can use multiple separator characters. The following example uses spaces, commas, periods, colons, and tabs as separating characters, which are passed to in an array. The loop at the bottom of the code displays each of the words in the returned array. -:::code language="csharp" interactive="try-dotnet-method" source="../../../samples/snippets/csharp/how-to/strings/ParseStringsUsingSplit.cs" id="Snippet3"::: +:::code language="csharp" interactive="try-dotnet-method" source="./snippets/strings/ParseStringsUsingSplit.cs" id="Snippet3"::: Consecutive instances of any separator produce the empty string in the output array: -:::code language="csharp" interactive="try-dotnet-method" source="../../../samples/snippets/csharp/how-to/strings/ParseStringsUsingSplit.cs" id="Snippet4"::: +:::code language="csharp" interactive="try-dotnet-method" source="./snippets/strings/ParseStringsUsingSplit.cs" id="Snippet4"::: can take an array of strings (character sequences that act as separators for parsing the target string, instead of single characters). -:::code language="csharp" interactive="try-dotnet-method" source="../../../samples/snippets/csharp/how-to/strings/ParseStringsUsingSplit.cs" id="Snippet5"::: +:::code language="csharp" interactive="try-dotnet-method" source="./snippets/strings/ParseStringsUsingSplit.cs" id="Snippet5"::: + +## Limit output size + +The following example shows how to limit the output to the first four substrings in the source string. + +:::code language="csharp" interactive="try-dotnet-method" source="./snippets/strings/ParseStringsUsingSplit.cs" id="Snippet6"::: + +## Remove empty substrings + +Consecutive separator characters produce the empty string as a value in the returned array. You can see how an empty string is created in the following example, which uses the space character as a separator. + +:::code language="csharp" interactive="try-dotnet-method" source="./snippets/strings/ParseStringsUsingSplit.cs" id="Snippet2"::: + +This behavior makes it easier for formats like comma-separated values (CSV) files representing tabular data. Consecutive commas represent a blank column. + +You can pass an optional parameter to exclude any empty strings in the returned array. For more complicated processing of the returned collection, you can use [LINQ](/dotnet/csharp/linq/) to manipulate the result sequence. + +## Trim whitespace + +The following example shows the effect of trimming entries: + +:::code language="csharp" interactive="try-dotnet-method" source="./snippets/strings/ParseStringsUsingSplit.cs" id="Snippet7"::: + +The untrimmed entries have extra whitespace before the numerals. ## Use GitHub Copilot to split a string diff --git a/docs/csharp/how-to/search-strings.md b/docs/csharp/how-to/search-strings.md index fe7cd0f298a9d..f41d2057bbdf3 100644 --- a/docs/csharp/how-to/search-strings.md +++ b/docs/csharp/how-to/search-strings.md @@ -1,12 +1,11 @@ --- title: "How to search strings" description: Learn about two strategies to search for text in strings in C#. String class methods search for specific text. Regular expressions search for patterns in text. -ms.date: 02/21/2018 +ms.date: 02/18/2025 helpviewer_keywords: - "searching strings [C#]" - "strings [C#], searching with String methods" - "strings [C#], searching with regular expressions" -ms.assetid: fb1d9a6d-598d-4a35-bd5f-b86012edcb2b --- # How to search strings @@ -15,13 +14,13 @@ You can use two main strategies to search for text in strings. Methods of the class, provides a number of useful methods for searching the contents of a string. Among them are , , , , . The class provides a rich vocabulary to search for patterns in text. In this article, you learn these techniques and how to choose the best method for your needs. +The [string](../language-reference/builtin-types/reference-types.md#the-string-type) type, which is an alias for the class, provides many useful methods for searching the contents of a string. Among them are , , , , . The class provides a rich vocabulary to search for patterns in text. In this article, you learn these techniques and how to choose the best method for your needs. ## Does a string contain text? The , , and methods search a string for specific text. The following example shows each of these methods and a variation that uses a case-insensitive search: -:::code language="csharp" interactive="try-dotnet-method" source="../../../samples/snippets/csharp/how-to/strings/SearchStrings.cs" id="Snippet1"::: +:::code language="csharp" interactive="try-dotnet-method" source="./snippets/strings/SearchStrings.cs" id="Snippet1"::: The preceding example demonstrates an important point for using these methods. Searches are **case-sensitive** by default. You use the enumeration value to specify a case-insensitive search. @@ -29,7 +28,7 @@ The preceding example demonstrates an important point for using these methods. S The and methods also search for text in strings. These methods return the location of the text being sought. If the text isn't found, they return `-1`. The following example shows a search for the first and last occurrence of the word "methods" and displays the text in between. -:::code language="csharp" interactive="try-dotnet-method" source="../../../samples/snippets/csharp/how-to/strings/SearchStrings.cs" id="Snippet2"::: +:::code language="csharp" interactive="try-dotnet-method" source="./snippets/strings/SearchStrings.cs" id="Snippet2"::: ## Finding specific text using regular expressions @@ -37,7 +36,7 @@ The cla The following code example searches for the word "the" or "their" in a sentence, ignoring case. The static method performs the search. You give it the string to search and a search pattern. In this case, a third argument specifies case-insensitive search. For more information, see . -The search pattern describes the text you search for. The following table describes each element of the search pattern. (The table below uses the single `\`, which must be escaped as `\\` in a C# string). +The search pattern describes the text you search for. The following table describes each element of the search pattern. (The following table uses the single `\`, which must be escaped as `\\` in a C# string). | Pattern | Meaning | |----------|----------------------------------| @@ -45,24 +44,24 @@ The search pattern describes the text you search for. The following table descri | `(eir)?` | match 0 or 1 occurrence of "eir" | | `\s` | match a white-space character | -:::code language="csharp" interactive="try-dotnet-method" source="../../../samples/snippets/csharp/how-to/strings/SearchStrings.cs" id="Snippet3"::: +:::code language="csharp" interactive="try-dotnet-method" source="./snippets/strings/SearchStrings.cs" id="Snippet3"::: > [!TIP] -> The `string` methods are usually better choices when you are searching for an exact string. Regular expressions are better when you are searching for some pattern in a source string. +> The `string` methods are generally better choices when you're searching for an exact string. Regular expressions are better when you're searching for some pattern in a source string. ## Does a string follow a pattern? -The following code uses regular expressions to validate the format of each string in an array. The validation requires that each string have the form of a telephone number in which three groups of digits are separated by dashes, the first two groups contain three digits, and the third group contains four digits. The search pattern uses the regular expression `^\\d{3}-\\d{3}-\\d{4}$`. For more information, see [Regular Expression Language - Quick Reference](../../standard/base-types/regular-expression-language-quick-reference.md). +The following code uses regular expressions to validate the format of each string in an array. The validation requires that each string is formatted as a telephone number: three groups of digits separated by dashes where the first two groups contain three digits and the third group contains four digits. The search pattern uses the regular expression `^\\d{3}-\\d{3}-\\d{4}$`. For more information, see [Regular Expression Language - Quick Reference](../../standard/base-types/regular-expression-language-quick-reference.md). | Pattern | Meaning | |---------|-------------------------------------| | `^` | matches the beginning of the string | -| `\d{3}` | matches exactly 3 digit characters | +| `\d{3}` | matches exactly three digit characters | | `-` | matches the '-' character | -| `\d{4}` | matches exactly 4 digit characters | +| `\d{4}` | matches exactly four digit characters | | `$` | matches the end of the string | -:::code language="csharp" interactive="try-dotnet-method" source="../../../samples/snippets/csharp/how-to/strings/SearchStrings.cs" id="Snippet4"::: +:::code language="csharp" interactive="try-dotnet-method" source="./snippets/\strings/SearchStrings.cs" id="Snippet4"::: This single search pattern matches many valid strings. Regular expressions are better to search for or validate against a pattern, rather than a single text string. diff --git a/docs/csharp/how-to/snippets/strings/CompareStrings.cs b/docs/csharp/how-to/snippets/strings/CompareStrings.cs new file mode 100644 index 0000000000000..032007728e697 --- /dev/null +++ b/docs/csharp/how-to/snippets/strings/CompareStrings.cs @@ -0,0 +1,338 @@ +namespace HowToStrings; + +public static class CompareStrings +{ + public static void Examples() + { + OrdinalDefaultComparisons(); + OrdinalIgnoreCaseComparisons(); + WordSortOrderInvariantCulture(); + LinguisticComparisons(); + + SortArrayOfStrings(); + SearchSortedArray(); + + SortListOfStrings(); + SearchSortedList(); + } + + private static void OrdinalDefaultComparisons() + { + // + string root = @"C:\users"; + string root2 = @"C:\Users"; + + bool result = root.Equals(root2); + Console.WriteLine($"Ordinal comparison: <{root}> and <{root2}> are {(result ? "equal." : "not equal.")}"); + + result = root.Equals(root2, StringComparison.Ordinal); + Console.WriteLine($"Ordinal comparison: <{root}> and <{root2}> are {(result ? "equal." : "not equal.")}"); + + Console.WriteLine($"Using == says that <{root}> and <{root2}> are {(root == root2 ? "equal" : "not equal")}"); + // + } + + private static void OrdinalIgnoreCaseComparisons() + { + // + string root = @"C:\users"; + string root2 = @"C:\Users"; + + bool result = root.Equals(root2, StringComparison.OrdinalIgnoreCase); + bool areEqual = String.Equals(root, root2, StringComparison.OrdinalIgnoreCase); + int comparison = String.Compare(root, root2, comparisonType: StringComparison.OrdinalIgnoreCase); + + Console.WriteLine($"Ordinal ignore case: <{root}> and <{root2}> are {(result ? "equal." : "not equal.")}"); + Console.WriteLine($"Ordinal static ignore case: <{root}> and <{root2}> are {(areEqual ? "equal." : "not equal.")}"); + if (comparison < 0) + { + Console.WriteLine($"<{root}> is less than <{root2}>"); + } + else if (comparison > 0) + { + Console.WriteLine($"<{root}> is greater than <{root2}>"); + } + else + { + Console.WriteLine($"<{root}> and <{root2}> are equivalent in order"); + } + // + } + + private static void WordSortOrderInvariantCulture() + { + // + string first = "Sie tanzen auf der Straße."; + string second = "Sie tanzen auf der Strasse."; + + Console.WriteLine($"First sentence is <{first}>"); + Console.WriteLine($"Second sentence is <{second}>"); + + bool equal = String.Equals(first, second, StringComparison.InvariantCulture); + Console.WriteLine($"The two strings {(equal == true ? "are" : "are not")} equal."); + showComparison(first, second); + + string word = "coop"; + string words = "co-op"; + string other = "cop"; + + showComparison(word, words); + showComparison(word, other); + showComparison(words, other); + void showComparison(string one, string two) + { + int compareLinguistic = String.Compare(one, two, StringComparison.InvariantCulture); + int compareOrdinal = String.Compare(one, two, StringComparison.Ordinal); + if (compareLinguistic < 0) + { + Console.WriteLine($"<{one}> is less than <{two}> using invariant culture"); + } + else if (compareLinguistic > 0) + { + Console.WriteLine($"<{one}> is greater than <{two}> using invariant culture"); + } + else + { + Console.WriteLine($"<{one}> and <{two}> are equivalent in order using invariant culture"); + } + + if (compareOrdinal < 0) + { + Console.WriteLine($"<{one}> is less than <{two}> using ordinal comparison"); + } + else if (compareOrdinal > 0) + { + Console.WriteLine($"<{one}> is greater than <{two}> using ordinal comparison"); + } + else + { + Console.WriteLine($"<{one}> and <{two}> are equivalent in order using ordinal comparison"); + } + } + // + } + private static void LinguisticComparisons() + { + // + string first = "Sie tanzen auf der Straße."; + string second = "Sie tanzen auf der Strasse."; + + Console.WriteLine($"First sentence is <{first}>"); + Console.WriteLine($"Second sentence is <{second}>"); + + var en = new System.Globalization.CultureInfo("en-US"); + + // For culture-sensitive comparisons, use the String.Compare + // overload that takes a StringComparison value. + int i = String.Compare(first, second, en, System.Globalization.CompareOptions.None); + Console.WriteLine($"Comparing in {en.Name} returns {i}."); + + var de = new System.Globalization.CultureInfo("de-DE"); + i = String.Compare(first, second, de, System.Globalization.CompareOptions.None); + Console.WriteLine($"Comparing in {de.Name} returns {i}."); + + bool b = String.Equals(first, second, StringComparison.CurrentCulture); + Console.WriteLine($"The two strings {(b ? "are" : "are not")} equal."); + + string word = "coop"; + string words = "co-op"; + string other = "cop"; + + showComparison(word, words, en); + showComparison(word, other, en); + showComparison(words, other, en); + void showComparison(string one, string two, System.Globalization.CultureInfo culture) + { + int compareLinguistic = String.Compare(one, two, en, System.Globalization.CompareOptions.None); + int compareOrdinal = String.Compare(one, two, StringComparison.Ordinal); + if (compareLinguistic < 0) + { + Console.WriteLine($"<{one}> is less than <{two}> using en-US culture"); + } + else if (compareLinguistic > 0) + { + Console.WriteLine($"<{one}> is greater than <{two}> using en-US culture"); + } + else + { + Console.WriteLine($"<{one}> and <{two}> are equivalent in order using en-US culture"); + } + + if (compareOrdinal < 0) + { + Console.WriteLine($"<{one}> is less than <{two}> using ordinal comparison"); + } + else if (compareOrdinal > 0) + { + Console.WriteLine($"<{one}> is greater than <{two}> using ordinal comparison"); + } + else + { + Console.WriteLine($"<{one}> and <{two}> are equivalent in order using ordinal comparison"); + } + } + // + } + + private static void SortArrayOfStrings() + { + // + string[] lines = + [ + @"c:\public\textfile.txt", + @"c:\public\textFile.TXT", + @"c:\public\Text.txt", + @"c:\public\testfile2.txt" + ]; + + Console.WriteLine("Non-sorted order:"); + foreach (string s in lines) + { + Console.WriteLine($" {s}"); + } + + Console.WriteLine("\n\rSorted order:"); + + // Specify Ordinal to demonstrate the different behavior. + Array.Sort(lines, StringComparer.CurrentCulture); + + foreach (string s in lines) + { + Console.WriteLine($" {s}"); + } + // + } + + private static void SearchSortedArray() + { + // + string[] lines = + [ + @"c:\public\textfile.txt", + @"c:\public\textFile.TXT", + @"c:\public\Text.txt", + @"c:\public\testfile2.txt" + ]; + Array.Sort(lines, StringComparer.CurrentCulture); + + string searchString = @"c:\public\TEXTFILE.TXT"; + Console.WriteLine($"Binary search for <{searchString}>"); + int result = Array.BinarySearch(lines, searchString, StringComparer.CurrentCulture); + ShowWhere(lines, result); + + Console.WriteLine($"{(result > 0 ? "Found" : "Did not find")} {searchString}"); + + void ShowWhere(T[] array, int index) + { + if (index < 0) + { + index = ~index; + + Console.Write("Not found. Sorts between: "); + + if (index == 0) + { + Console.Write("beginning of sequence and "); + } + else + { + Console.Write($"{array[index - 1]} and "); + } + + if (index == array.Length) + { + Console.WriteLine("end of sequence."); + } + else + { + Console.WriteLine($"{array[index]}."); + } + } + else + { + Console.WriteLine($"Found at index {index}."); + } + } + // + } + + private static void SortListOfStrings() + { + // + List lines = + [ + @"c:\public\textfile.txt", + @"c:\public\textFile.TXT", + @"c:\public\Text.txt", + @"c:\public\testfile2.txt" + ]; + + Console.WriteLine("Non-sorted order:"); + foreach (string s in lines) + { + Console.WriteLine($" {s}"); + } + + Console.WriteLine("\n\rSorted order:"); + + lines.Sort((left, right) => left.CompareTo(right)); + foreach (string s in lines) + { + Console.WriteLine($" {s}"); + } + // + } + + private static void SearchSortedList() + { + // + List lines = + [ + @"c:\public\textfile.txt", + @"c:\public\textFile.TXT", + @"c:\public\Text.txt", + @"c:\public\testfile2.txt" + ]; + lines.Sort((left, right) => left.CompareTo(right)); + + string searchString = @"c:\public\TEXTFILE.TXT"; + Console.WriteLine($"Binary search for <{searchString}>"); + int result = lines.BinarySearch(searchString); + ShowWhere(lines, result); + + Console.WriteLine($"{(result > 0 ? "Found" : "Did not find")} {searchString}"); + + void ShowWhere(IList collection, int index) + { + if (index < 0) + { + index = ~index; + + Console.Write("Not found. Sorts between: "); + + if (index == 0) + { + Console.Write("beginning of sequence and "); + } + else + { + Console.Write($"{collection[index - 1]} and "); + } + + if (index == collection.Count) + { + Console.WriteLine("end of sequence."); + } + else + { + Console.WriteLine($"{collection[index]}."); + } + } + else + { + Console.WriteLine($"Found at index {index}."); + } + } + // + } +} diff --git a/docs/csharp/how-to/snippets/strings/Concatenate.cs b/docs/csharp/how-to/snippets/strings/Concatenate.cs new file mode 100644 index 0000000000000..b3e10829c5b20 --- /dev/null +++ b/docs/csharp/how-to/snippets/strings/Concatenate.cs @@ -0,0 +1,100 @@ +using System.Text; + +namespace HowToStrings; + +static public class Concatenate +{ + public static void Examples() + { + UsingAddWithConstantStrings(); + UsingAddWithVariables(); + UsingInterpolationWithVariables(); + UsingStringBuilder(); + UsingConcatAndJoin(); + UsingAggregate(); + } + + private static void UsingAddWithConstantStrings() + { + // + // Concatenation of literals is performed at compile time, not run time. + string text = "Historically, the world of data and the world of objects " + + "have not been well integrated. Programmers work in C# or Visual Basic " + + "and also in SQL or XQuery. On the one side are concepts such as classes, " + + "objects, fields, inheritance, and .NET Framework APIs. On the other side " + + "are tables, columns, rows, nodes, and separate languages for dealing with " + + "them. Data types often require translation between the two worlds; there are " + + "different standard functions. Because the object world has no notion of query, a " + + "query can only be represented as a string without compile-time type checking or " + + "IntelliSense support in the IDE. Transferring data from SQL tables or XML trees to " + + "objects in memory is often tedious and error-prone."; + + Console.WriteLine(text); + // + } + + private static void UsingAddWithVariables() + { + // + string userName = ""; + string dateString = DateTime.Today.ToShortDateString(); + + // Use the + and += operators for one-time concatenations. + string str = "Hello " + userName + ". Today is " + dateString + "."; + Console.WriteLine(str); + + str += " How are you today?"; + Console.WriteLine(str); + // + } + private static void UsingInterpolationWithVariables() + { + // + string userName = ""; + string date = DateTime.Today.ToShortDateString(); + + // Use string interpolation to concatenate strings. + string str = $"Hello {userName}. Today is {date}."; + Console.WriteLine(str); + + str = $"{str} How are you today?"; + Console.WriteLine(str); + // + } + + private static void UsingStringBuilder() + { + // + // Use StringBuilder for concatenation in tight loops. + var sb = new StringBuilder(); + for (int i = 0; i < 20; i++) + { + sb.AppendLine(i.ToString()); + } + Console.WriteLine(sb.ToString()); + // + } + + private static void UsingConcatAndJoin() + { + // + string[] words = ["The", "quick", "brown", "fox", "jumps", "over", "the", "lazy", "dog."]; + + var unreadablePhrase = string.Concat(words); + Console.WriteLine(unreadablePhrase); + + var readablePhrase = string.Join(" ", words); + Console.WriteLine(readablePhrase); + // + } + + private static void UsingAggregate() + { + // + string[] words = ["The", "quick", "brown", "fox", "jumps", "over", "the", "lazy", "dog."]; + + var phrase = words.Aggregate((partialPhrase, word) =>$"{partialPhrase} {word}"); + Console.WriteLine(phrase); + // + } +} diff --git a/docs/csharp/how-to/snippets/strings/ModifyStrings.cs b/docs/csharp/how-to/snippets/strings/ModifyStrings.cs new file mode 100644 index 0000000000000..68982c1dd1e60 --- /dev/null +++ b/docs/csharp/how-to/snippets/strings/ModifyStrings.cs @@ -0,0 +1,144 @@ +namespace HowToStrings; + +public static class ModifyStrings +{ + public static void Examples() + { + ReplaceCreatesNewString(); + ReplaceChars(); + TrimWhitespace(); + RemoveText(); + ReplaceWithRegEx(); + ReplaceCharArray(); + UsingStringCreate(); + } + + private static void ReplaceCreatesNewString() + { + // + string source = "The mountains are behind the clouds today."; + + // Replace one substring with another with String.Replace. + // Only exact matches are supported. + var replacement = source.Replace("mountains", "peaks"); + Console.WriteLine($"The source string is <{source}>"); + Console.WriteLine($"The updated string is <{replacement}>"); + // + } + + private static void ReplaceChars() + { + // + string source = "The mountains are behind the clouds today."; + + // Replace all occurrences of one char with another. + var replacement = source.Replace(' ', '_'); + Console.WriteLine(source); + Console.WriteLine(replacement); + // + } + + private static void TrimWhitespace() + { + // + // Remove trailing and leading white space. + string source = " I'm wider than I need to be. "; + // Store the results in a new string variable. + var trimmedResult = source.Trim(); + var trimLeading = source.TrimStart(); + var trimTrailing = source.TrimEnd(); + Console.WriteLine($"<{source}>"); + Console.WriteLine($"<{trimmedResult}>"); + Console.WriteLine($"<{trimLeading}>"); + Console.WriteLine($"<{trimTrailing}>"); + // + } + + private static void RemoveText() + { + // + string source = "Many mountains are behind many clouds today."; + // Remove a substring from the middle of the string. + string toRemove = "many "; + string result = string.Empty; + int i = source.IndexOf(toRemove); + if (i >= 0) + { + result= source.Remove(i, toRemove.Length); + } + Console.WriteLine(source); + Console.WriteLine(result); + // + } + + private static void ReplaceWithRegEx() + { + // + string source = "The mountains are still there behind the clouds today."; + + // Use Regex.Replace for more flexibility. + // Replace "the" or "The" with "many" or "Many". + // using System.Text.RegularExpressions + string replaceWith = "many "; + source = System.Text.RegularExpressions.Regex.Replace(source, """the\s""", LocalReplaceMatchCase, + System.Text.RegularExpressions.RegexOptions.IgnoreCase); + Console.WriteLine(source); + + string LocalReplaceMatchCase(System.Text.RegularExpressions.Match matchExpression) + { + // Test whether the match is capitalized + if (Char.IsUpper(matchExpression.Value[0])) + { + // Capitalize the replacement string + System.Text.StringBuilder replacementBuilder = new System.Text.StringBuilder(replaceWith); + replacementBuilder[0] = Char.ToUpper(replacementBuilder[0]); + return replacementBuilder.ToString(); + } + else + { + return replaceWith; + } + } + // + } + + private static void ReplaceCharArray() + { + // + string phrase = "The quick brown fox jumps over the fence"; + Console.WriteLine(phrase); + + char[] phraseAsChars = phrase.ToCharArray(); + int animalIndex = phrase.IndexOf("fox"); + if (animalIndex != -1) + { + phraseAsChars[animalIndex++] = 'c'; + phraseAsChars[animalIndex++] = 'a'; + phraseAsChars[animalIndex] = 't'; + } + + string updatedPhrase = new string(phraseAsChars); + Console.WriteLine(updatedPhrase); + // + } + + private static void UsingStringCreate() + { + // + // constructing a string from a char array, prefix it with some additional characters + char[] chars = [ 'a', 'b', 'c', 'd', '\0' ]; + int length = chars.Length + 2; + string result = string.Create(length, chars, (Span strContent, char[] charArray) => + { + strContent[0] = '0'; + strContent[1] = '1'; + for (int i = 0; i < charArray.Length; i++) + { + strContent[i + 2] = charArray[i]; + } + }); + + Console.WriteLine(result); + // + } +} diff --git a/docs/csharp/how-to/snippets/strings/ParseStringsUsingSplit.cs b/docs/csharp/how-to/snippets/strings/ParseStringsUsingSplit.cs new file mode 100644 index 0000000000000..3e09ff2d24cd4 --- /dev/null +++ b/docs/csharp/how-to/snippets/strings/ParseStringsUsingSplit.cs @@ -0,0 +1,165 @@ +namespace HowToStrings; + +public static class ParseStringsUsingSplit +{ + public static void Examples() + { + Console.WriteLine("Split words"); + Console.WriteLine(); + SplitWords(); + + Console.WriteLine("Enumerate words"); + Console.WriteLine(); + EnumerateWords(); + + Console.WriteLine("Split words with repeated separators"); + Console.WriteLine(); + SplitWordsWithRepeatedSeparators(); + + Console.WriteLine("Split on multiple chars"); + Console.WriteLine(); + SplitOnMultipleChars(); + + Console.WriteLine("Split on multiple chars with gaps"); + Console.WriteLine(); + SplitOnMultipleCharsWithGaps(); + + Console.WriteLine("Split using strings"); + Console.WriteLine(); + SplitUsingStrings(); + + Console.WriteLine("Split into no more than four substrings"); + Console.WriteLine(); + SplitFourTimes(); + + Console.WriteLine("Trim output substrings"); + Console.WriteLine(); + SplitAndTrim(); + } + + private static void SplitWords() + { + // + string phrase = "The quick brown fox jumps over the lazy dog."; + string[] words = phrase.Split(' '); + + foreach (var word in words) + { + Console.WriteLine($"<{word}>"); + } + // + } + + private static void EnumerateWords() + { + // + string phrase = "The quick brown fox jumps over the lazy dog."; + string[] words = phrase.Split(' '); + + for (int i = 0; i < words.Length; i++) + { + Console.WriteLine($"Index {i}: <{words[i]}>"); + } + // + } + + private static void SplitWordsWithRepeatedSeparators() + { + // + string phrase = "The quick brown fox jumps over the lazy dog."; + string[] words = phrase.Split(' '); + + foreach (var word in words) + { + Console.WriteLine($"<{word}>"); + } + // + } + + private static void SplitOnMultipleChars() + { + // + char[] delimiterChars = [' ', ',', '.', ':', '\t']; + + string text = "one\ttwo three:four,five six seven"; + Console.WriteLine($"Original text: '{text}'"); + + string[] words = text.Split(delimiterChars); + Console.WriteLine($"{words.Length} words in text:"); + + foreach (var word in words) + { + Console.WriteLine($"<{word}>"); + } + // + } + + private static void SplitOnMultipleCharsWithGaps() + { + // + char[] delimiterChars = [' ', ',', '.', ':', '\t']; + + string text = "one\ttwo :,five six seven"; + Console.WriteLine($"Original text: '{text}'"); + + string[] words = text.Split(delimiterChars); + Console.WriteLine($"{words.Length} words in text:"); + + foreach (var word in words) + { + Console.WriteLine($"<{word}>"); + } + // + } + + private static void SplitUsingStrings() + { + // + string[] separatingStrings = ["<<", "..."]; + + string text = "one< + } + + private static void SplitFourTimes() + { + // + string phrase = "The quick brown fox jumps over the lazy dog."; + string[] words = phrase.Split(' ', 4, StringSplitOptions.None); + + foreach (var word in words) + { + Console.WriteLine($"<{word}>"); + } + // + } + + private static void SplitAndTrim() + { + // + string numerals = "1, 2, 3, 4, 5, 6, 7, 8, 9, 10"; + string[] words = numerals.Split(',', StringSplitOptions.TrimEntries); + + Console.WriteLine("Trimmed entries:"); + foreach (var word in words) + { + Console.WriteLine($"<{word}>"); + } + words = numerals.Split(',', StringSplitOptions.None); + Console.WriteLine("Untrimmed entries:"); + foreach (var word in words) + { + Console.WriteLine($"<{word}>"); + } + // + } +} diff --git a/docs/csharp/how-to/snippets/strings/Program.cs b/docs/csharp/how-to/snippets/strings/Program.cs new file mode 100644 index 0000000000000..78067d0124cfb --- /dev/null +++ b/docs/csharp/how-to/snippets/strings/Program.cs @@ -0,0 +1,17 @@ +using HowToStrings; + +Console.WriteLine("============================ String.Split examples ================================================="); +Console.WriteLine(); +ParseStringsUsingSplit.Examples(); +Console.WriteLine("============================ String concatenation examples ================================================="); +Console.WriteLine(); +Concatenate.Examples(); +Console.WriteLine("============================ String Searching examples ================================================="); +Console.WriteLine(); +SearchStrings.Examples(); +Console.WriteLine("============================ Modify string examples ================================================="); +Console.WriteLine(); +ModifyStrings.Examples(); +Console.WriteLine("============================ Compare string examples ================================================="); +Console.WriteLine(); +CompareStrings.Examples(); diff --git a/docs/csharp/how-to/snippets/strings/SearchStrings.cs b/docs/csharp/how-to/snippets/strings/SearchStrings.cs new file mode 100644 index 0000000000000..ce92fe7d03d33 --- /dev/null +++ b/docs/csharp/how-to/snippets/strings/SearchStrings.cs @@ -0,0 +1,117 @@ +namespace HowToStrings; + +public class SearchStrings +{ + public static void Examples() + { + SearchWithMethods(); + SearchByIndex(); + RegularExpressionsOne(); + RegularExpressionsValidation(); + } + + private static void SearchWithMethods() + { + // + string factMessage = "Extension methods have all the capabilities of regular static methods."; + + // Write the string and include the quotation marks. + Console.WriteLine($"\"{factMessage}\""); + + // Simple comparisons are always case sensitive! + bool containsSearchResult = factMessage.Contains("extension"); + // Raw string literals can work here because the output doesn't begin with " + Console.WriteLine($"""Contains "extension"? {containsSearchResult}"""); + + // For user input and strings that will be displayed to the end user, + // use the StringComparison parameter on methods that have it to specify how to match strings. + bool ignoreCaseSearchResult = factMessage.StartsWith("extension", System.StringComparison.CurrentCultureIgnoreCase); + Console.WriteLine($"""Starts with "extension"? {ignoreCaseSearchResult} (ignoring case)"""); + + bool endsWithSearchResult = factMessage.EndsWith(".", System.StringComparison.CurrentCultureIgnoreCase); + Console.WriteLine($"Ends with '.'? {endsWithSearchResult}"); + // + } + + private static void SearchByIndex() + { + // + string factMessage = "Extension methods have all the capabilities of regular static methods."; + + // Write the string and include the quotation marks. + Console.WriteLine($"\"{factMessage}\""); + + // This search returns the substring between two strings, so + // the first index is moved to the character just after the first string. + int first = factMessage.IndexOf("methods") + "methods".Length; + int last = factMessage.LastIndexOf("methods"); + string str2 = factMessage.Substring(first, last - first); + Console.WriteLine($"""Substring between "methods" and "methods": '{str2}'"""); + // + } + + private static void RegularExpressionsOne() + { + // + string[] sentences = + [ + "Put the water over there.", + "They're quite thirsty.", + "Their water bottles broke." + ]; + + string sPattern = "the(ir)?\\s"; + + foreach (string s in sentences) + { + Console.Write($"{s,24}"); + + if (System.Text.RegularExpressions.Regex.IsMatch(s, sPattern, System.Text.RegularExpressions.RegexOptions.IgnoreCase)) + { + Console.WriteLine($" (match for '{sPattern}' found)"); + } + else + { + Console.WriteLine(); + } + } + // + } + + private static void RegularExpressionsValidation() + { + // + string[] numbers = + [ + "123-555-0190", + "444-234-22450", + "690-555-0178", + "146-893-232", + "146-555-0122", + "4007-555-0111", + "407-555-0111", + "407-2-5555", + "407-555-8974", + "407-2ab-5555", + "690-555-8148", + "146-893-232-" + ]; + + string sPattern = """^\d{3}-\d{3}-\d{4}$"""; + + foreach (string s in numbers) + { + Console.Write($"{s,14}"); + + if (System.Text.RegularExpressions.Regex.IsMatch(s, sPattern)) + { + Console.WriteLine(" - valid"); + } + else + { + Console.WriteLine(" - invalid"); + } + } + // + } +} diff --git a/samples/snippets/csharp/how-to/strings/strings.csproj b/docs/csharp/how-to/snippets/strings/strings.csproj similarity index 62% rename from samples/snippets/csharp/how-to/strings/strings.csproj rename to docs/csharp/how-to/snippets/strings/strings.csproj index bccebfef5e970..eeafa373ee0cf 100644 --- a/samples/snippets/csharp/how-to/strings/strings.csproj +++ b/docs/csharp/how-to/snippets/strings/strings.csproj @@ -1,9 +1,10 @@ - + Exe - net8.0 + net9.0 enable + enable HowToStrings true diff --git a/docs/csharp/language-reference/operators/equality-operators.md b/docs/csharp/language-reference/operators/equality-operators.md index c31e9ee511353..081b5253798bf 100644 --- a/docs/csharp/language-reference/operators/equality-operators.md +++ b/docs/csharp/language-reference/operators/equality-operators.md @@ -1,7 +1,7 @@ --- title: "Equality operators - test if two objects are equal or not equal" description: "C# equality operators test if two objects are equal or not equal. You can define equality operators for your types for custom comparisons for equality" -ms.date: 11/29/2022 +ms.date: 02/18/2025 author: pkulikov f1_keywords: - "==_CSharpKeyword" @@ -20,6 +20,8 @@ helpviewer_keywords: The [`==` (equality)](#equality-operator-) and [`!=` (inequality)](#inequality-operator-) operators check if their operands are equal or not. Value types are equal when their contents are equal. Reference types are equal when the two variables refer to the same storage. +You can use the [`is`](./is.md) pattern matching operator as an alternative to an `==` test when you test against a [constant value](./patterns.md#constant-pattern). The `is` operator uses the default equality semantics for all value and reference types. + ## Equality operator == The equality operator `==` returns `true` if its operands are equal, `false` otherwise. @@ -28,22 +30,22 @@ The equality operator `==` returns `true` if its operands are equal, `false` oth Operands of the [built-in value types](../builtin-types/value-types.md#built-in-value-types) are equal if their values are equal: -[!code-csharp-interactive[value types equality](snippets/shared/EqualityOperators.cs#ValueTypesEquality)] +:::code language="csharp" interactive="try-dotnet-method" source="snippets/shared/EqualityOperators.cs" id="ValueTypesEquality"::: > [!NOTE] -> For the `==`, [`<`, `>`, `<=`, and `>=`](comparison-operators.md) operators, if any of the operands is not a number ( or ), the result of operation is `false`. That means that the `NaN` value is neither greater than, less than, nor equal to any other `double` (or `float`) value, including `NaN`. For more information and examples, see the or reference article. +> For the `==`, [`<`, `>`, `<=`, and `>=`](comparison-operators.md) operators, if any of the operands isn't a number ( or ), the result of operation is `false`. That means that the `NaN` value isn't greater than, less than, or equal to any other `double` (or `float`) value, including `NaN`. For more information and examples, see the or reference article. Two operands of the same [enum](../builtin-types/enum.md) type are equal if the corresponding values of the underlying integral type are equal. User-defined [struct](../builtin-types/struct.md) types don't support the `==` operator by default. To support the `==` operator, a user-defined struct must [overload](operator-overloading.md) it. -The `==` and `!=` operators are supported by C# [tuples](../builtin-types/value-tuples.md). For more information, see the [Tuple equality](../builtin-types/value-tuples.md#tuple-equality) section of the [Tuple types](../builtin-types/value-tuples.md) article. +C# [tuples](../builtin-types/value-tuples.md) have built-in support for the `==` and `!=` operators. For more information, see the [Tuple equality](../builtin-types/value-tuples.md#tuple-equality) section of the [Tuple types](../builtin-types/value-tuples.md) article. ### Reference types equality -By default, two non-record reference-type operands are equal if they refer to the same object: +By default, reference-type operands, excluding records, are equal if they refer to the same object: -[!code-csharp[reference type equality](snippets/shared/EqualityOperators.cs#ReferenceTypesEquality)] +:::code language="csharp" source="snippets/shared/EqualityOperators.cs" id="ReferenceTypesEquality"::: As the example shows, user-defined reference types support the `==` operator by default. However, a reference type can overload the `==` operator. If a reference type overloads the `==` operator, use the method to check if two references of that type refer to the same object. @@ -53,13 +55,13 @@ As the example shows, user-defined reference types support the `==` operator by :::code language="csharp" source="snippets/shared/EqualityOperators.cs" id="RecordTypesEquality"::: -As the preceding example shows, for non-record reference-type members their reference values are compared, not the referenced instances. +As the preceding example shows, for reference-type members their reference values are compared, not the referenced instances. ### String equality Two [string](../builtin-types/reference-types.md#the-string-type) operands are equal when both of them are `null` or both string instances are of the same length and have identical characters in each character position: -[!code-csharp-interactive[string equality](snippets/shared/EqualityOperators.cs#StringEquality)] +:::code language="csharp" interactive="try-dotnet-method" source="snippets/shared/EqualityOperators.cs" id="StringEquality"::: String equality comparisons are case-sensitive ordinal comparisons. For more information about string comparison, see [How to compare strings in C#](../../how-to/compare-strings.md). @@ -67,21 +69,26 @@ String equality comparisons are case-sensitive ordinal comparisons. For more inf Two [delegate](../../programming-guide/delegates/index.md) operands of the same run-time type are equal when both of them are `null` or their invocation lists are of the same length and have equal entries in each position: -[!code-csharp-interactive[delegate equality](snippets/shared/EqualityOperators.cs#DelegateEquality)] +:::code language="csharp" interactive="try-dotnet-method" source="snippets/shared/EqualityOperators.cs" id="DelegateEquality"::: + +> [!IMPORTANT] +> Equal entries in an invocation list include all fixed parameters in the invocation, including the receiver. The receiver is the instance of an object represented by `this` when the entry is invoked. + +:::code language="csharp" interactive="try-dotnet-method" source="snippets/shared/EqualityOperators.cs" id="SnippetCheckReceiver"::: For more information, see the [Delegate equality operators](~/_csharpstandard/standard/expressions.md#12129-delegate-equality-operators) section of the [C# language specification](~/_csharpstandard/standard/README.md). Delegates that are produced from evaluation of semantically identical [lambda expressions](lambda-expressions.md) aren't equal, as the following example shows: -[!code-csharp-interactive[from identical lambdas](snippets/shared/EqualityOperators.cs#IdenticalLambdas)] +:::code language="csharp" interactive="try-dotnet-method" source="snippets/shared/EqualityOperators.cs" id="IdenticalLambdas"::: -## Inequality operator != +## Inequality operator `!=` The inequality operator `!=` returns `true` if its operands aren't equal, `false` otherwise. For the operands of the [built-in types](../builtin-types/built-in-types.md), the expression `x != y` produces the same result as the expression `!(x == y)`. For more information about type equality, see the [Equality operator](#equality-operator-) section. The following example demonstrates the usage of the `!=` operator: -[!code-csharp-interactive[non-equality examples](snippets/shared/EqualityOperators.cs#NonEquality)] +:::code language="csharp" interactive="try-dotnet-method" source="snippets/shared/EqualityOperators.cs" id="NonEquality"::: ## Operator overloadability diff --git a/docs/csharp/language-reference/operators/is.md b/docs/csharp/language-reference/operators/is.md index 95203660c6f3d..8bf9a7dc44b7a 100644 --- a/docs/csharp/language-reference/operators/is.md +++ b/docs/csharp/language-reference/operators/is.md @@ -1,14 +1,14 @@ --- title: "The `is` operator - Match an expression against a type or constant pattern" description: "Learn about the C# `is` operator that matches an expression against a pattern. The `is` operator returns true when the expression matches the pattern." -ms.date: 11/28/2022 +ms.date: 02/18/2025 f1_keywords: - "is_CSharpKeyword" - "is" helpviewer_keywords: - "is keyword [C#]" --- -# is operator (C# reference) +# The `is` operator (C# reference) The `is` operator checks if the result of an expression is compatible with a given type. For information about the type-testing `is` operator, see the [is operator](type-testing-and-cast.md#is-operator) section of the [Type-testing and cast operators](type-testing-and-cast.md) article. You can also use the `is` operator to match an expression against a pattern, as the following example shows: diff --git a/docs/csharp/language-reference/operators/patterns.md b/docs/csharp/language-reference/operators/patterns.md index 7d578c523fad4..fc95dfde2fe6d 100644 --- a/docs/csharp/language-reference/operators/patterns.md +++ b/docs/csharp/language-reference/operators/patterns.md @@ -1,7 +1,7 @@ --- title: "Patterns - Pattern matching using the is and switch expressions." description: "Learn about the patterns supported by the `is` and `switch` expressions. Combine multiple patterns using the `and`, `or`, and `not` operators." -ms.date: 11/22/2024 +ms.date: 02/18/2025 f1_keywords: - "and_CSharpKeyword" - "or_CSharpKeyword" @@ -24,16 +24,16 @@ The following C# expressions and statements support pattern matching: In those constructs, you can match an input expression against any of the following patterns: -- [Declaration pattern](#declaration-and-type-patterns): to check the run-time type of an expression and, if a match succeeds, assign an expression result to a declared variable. -- [Type pattern](#declaration-and-type-patterns): to check the run-time type of an expression. -- [Constant pattern](#constant-pattern): to test that an expression result equals a specified constant. -- [Relational patterns](#relational-patterns): to compare an expression result with a specified constant. -- [Logical patterns](#logical-patterns): to test that an expression matches a logical combination of patterns. -- [Property pattern](#property-pattern): to test that an expression's properties or fields match nested patterns. -- [Positional pattern](#positional-pattern): to deconstruct an expression result and test if the resulting values match nested patterns. -- [`var` pattern](#var-pattern): to match any expression and assign its result to a declared variable. -- [Discard pattern](#discard-pattern): to match any expression. -- [List patterns](#list-patterns): to test that a sequence of elements matches corresponding nested patterns. Introduced in C# 11. +- [Declaration pattern](#declaration-and-type-patterns): check the run-time type of an expression and, if a match succeeds, assign an expression result to a declared variable. +- [Type pattern](#declaration-and-type-patterns): check the run-time type of an expression. +- [Constant pattern](#constant-pattern): test that an expression result equals a specified constant. +- [Relational patterns](#relational-patterns): compare an expression result with a specified constant. +- [Logical patterns](#logical-patterns): test that an expression matches a logical combination of patterns. +- [Property pattern](#property-pattern): test that an expression's properties or fields match nested patterns. +- [Positional pattern](#positional-pattern): deconstruct an expression result and test if the resulting values match nested patterns. +- [`var` pattern](#var-pattern): match any expression and assign its result to a declared variable. +- [Discard pattern](#discard-pattern): match any expression. +- [List patterns](#list-patterns): test that a sequence of elements matches corresponding nested patterns. Introduced in C# 11. [Logical](#logical-patterns), [property](#property-pattern), [positional](#positional-pattern), and [list](#list-patterns) patterns are *recursive* patterns. That is, they can contain *nested* patterns. @@ -81,7 +81,7 @@ For more information, see the [Declaration pattern](~/_csharplang/proposals/csha ## Constant pattern -You use a *constant pattern* to test if an expression result equals a specified constant, as the following example shows: +The *constant pattern* is an alternative syntax for [`==`](./equality-operators.md) when the right operand is a constant. You use a *constant pattern* to test if an expression result equals a specified constant, as the following example shows: :::code language="csharp" source="snippets/patterns/ConstantPattern.cs" id="BasicExample"::: @@ -184,7 +184,7 @@ You can also add a run-time type check and a variable declaration to a property :::code language="csharp" source="snippets/patterns/PropertyPattern.cs" id="WithTypeCheck"::: -A property pattern is a recursive pattern. That is, you can use any pattern as a nested pattern. Use a property pattern to match parts of data against nested patterns, as the following example shows: +A property pattern is a recursive pattern. You can use any pattern as a nested pattern. Use a property pattern to match parts of data against nested patterns, as the following example shows: :::code language="csharp" source="snippets/patterns/PropertyPattern.cs" id="RecursivePropertyPattern"::: diff --git a/docs/csharp/language-reference/operators/snippets/shared/EqualityOperators.cs b/docs/csharp/language-reference/operators/snippets/shared/EqualityOperators.cs index 720c4e87fca0b..fb1c593243041 100644 --- a/docs/csharp/language-reference/operators/snippets/shared/EqualityOperators.cs +++ b/docs/csharp/language-reference/operators/snippets/shared/EqualityOperators.cs @@ -103,6 +103,15 @@ private static void DelegateEquality() Console.WriteLine(object.ReferenceEquals(b, c)); // output: False Console.WriteLine(b == c); // output: True // + + // + var o1 = new object(); + var o2 = new object(); + var d1 = o1.ToString; + var d2 = o2.ToString; + Console.WriteLine(object.ReferenceEquals(d1, d2)); // output: False + Console.WriteLine(d1 == d2); // output: False (different receivers) + // } private static void IdenticalLambdasNotEqual() diff --git a/docs/csharp/language-reference/operators/snippets/shared/operators.csproj b/docs/csharp/language-reference/operators/snippets/shared/operators.csproj index bae8c4ac2f940..743c3a8f38201 100644 --- a/docs/csharp/language-reference/operators/snippets/shared/operators.csproj +++ b/docs/csharp/language-reference/operators/snippets/shared/operators.csproj @@ -1,8 +1,8 @@ - + Exe - net8.0 + net9.0 enable true true diff --git a/samples/snippets/csharp/how-to/strings/CompareStrings.cs b/samples/snippets/csharp/how-to/strings/CompareStrings.cs deleted file mode 100644 index 1fb3568e3a18d..0000000000000 --- a/samples/snippets/csharp/how-to/strings/CompareStrings.cs +++ /dev/null @@ -1,296 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Runtime.CompilerServices; -using System.Text; - -namespace HowToStrings -{ - public static class CompareStrings - { - public static void Examples() - { - OrdinalDefaultComparisons(); - OrdinalIgnoreCaseComparisons(); - WordSortOrderInvariantCulture(); - LinguisticComparisons(); - - SortArrayOfStrings(); - SearchSortedArray(); - - SortListOfStrings(); - SearchSortedList(); - } - - private static void OrdinalDefaultComparisons() - { - // - string root = @"C:\users"; - string root2 = @"C:\Users"; - - bool result = root.Equals(root2); - Console.WriteLine($"Ordinal comparison: <{root}> and <{root2}> are {(result ? "equal." : "not equal.")}"); - - result = root.Equals(root2, StringComparison.Ordinal); - Console.WriteLine($"Ordinal comparison: <{root}> and <{root2}> are {(result ? "equal." : "not equal.")}"); - - Console.WriteLine($"Using == says that <{root}> and <{root2}> are {(root == root2 ? "equal" : "not equal")}"); - // - } - - private static void OrdinalIgnoreCaseComparisons() - { - // - string root = @"C:\users"; - string root2 = @"C:\Users"; - - bool result = root.Equals(root2, StringComparison.OrdinalIgnoreCase); - bool areEqual = String.Equals(root, root2, StringComparison.OrdinalIgnoreCase); - int comparison = String.Compare(root, root2, comparisonType: StringComparison.OrdinalIgnoreCase); - - Console.WriteLine($"Ordinal ignore case: <{root}> and <{root2}> are {(result ? "equal." : "not equal.")}"); - Console.WriteLine($"Ordinal static ignore case: <{root}> and <{root2}> are {(areEqual ? "equal." : "not equal.")}"); - if (comparison < 0) - Console.WriteLine($"<{root}> is less than <{root2}>"); - else if (comparison > 0) - Console.WriteLine($"<{root}> is greater than <{root2}>"); - else - Console.WriteLine($"<{root}> and <{root2}> are equivalent in order"); - // - } - - private static void WordSortOrderInvariantCulture() - { - // - string first = "Sie tanzen auf der Straße."; - string second = "Sie tanzen auf der Strasse."; - - Console.WriteLine($"First sentence is <{first}>"); - Console.WriteLine($"Second sentence is <{second}>"); - - bool equal = String.Equals(first, second, StringComparison.InvariantCulture); - Console.WriteLine($"The two strings {(equal == true ? "are" : "are not")} equal."); - showComparison(first, second); - - string word = "coop"; - string words = "co-op"; - string other = "cop"; - - showComparison(word, words); - showComparison(word, other); - showComparison(words, other); - void showComparison(string one, string two) - { - int compareLinguistic = String.Compare(one, two, StringComparison.InvariantCulture); - int compareOrdinal = String.Compare(one, two, StringComparison.Ordinal); - if (compareLinguistic < 0) - Console.WriteLine($"<{one}> is less than <{two}> using invariant culture"); - else if (compareLinguistic > 0) - Console.WriteLine($"<{one}> is greater than <{two}> using invariant culture"); - else - Console.WriteLine($"<{one}> and <{two}> are equivalent in order using invariant culture"); - if (compareOrdinal < 0) - Console.WriteLine($"<{one}> is less than <{two}> using ordinal comparison"); - else if (compareOrdinal > 0) - Console.WriteLine($"<{one}> is greater than <{two}> using ordinal comparison"); - else - Console.WriteLine($"<{one}> and <{two}> are equivalent in order using ordinal comparison"); - } - // - } - private static void LinguisticComparisons() - { - // - string first = "Sie tanzen auf der Straße."; - string second = "Sie tanzen auf der Strasse."; - - Console.WriteLine($"First sentence is <{first}>"); - Console.WriteLine($"Second sentence is <{second}>"); - - var en = new System.Globalization.CultureInfo("en-US"); - - // For culture-sensitive comparisons, use the String.Compare - // overload that takes a StringComparison value. - int i = String.Compare(first, second, en, System.Globalization.CompareOptions.None); - Console.WriteLine($"Comparing in {en.Name} returns {i}."); - - var de = new System.Globalization.CultureInfo("de-DE"); - i = String.Compare(first, second, de, System.Globalization.CompareOptions.None); - Console.WriteLine($"Comparing in {de.Name} returns {i}."); - - bool b = String.Equals(first, second, StringComparison.CurrentCulture); - Console.WriteLine($"The two strings {(b ? "are" : "are not")} equal."); - - string word = "coop"; - string words = "co-op"; - string other = "cop"; - - showComparison(word, words, en); - showComparison(word, other, en); - showComparison(words, other, en); - void showComparison(string one, string two, System.Globalization.CultureInfo culture) - { - int compareLinguistic = String.Compare(one, two, en, System.Globalization.CompareOptions.None); - int compareOrdinal = String.Compare(one, two, StringComparison.Ordinal); - if (compareLinguistic < 0) - Console.WriteLine($"<{one}> is less than <{two}> using en-US culture"); - else if (compareLinguistic > 0) - Console.WriteLine($"<{one}> is greater than <{two}> using en-US culture"); - else - Console.WriteLine($"<{one}> and <{two}> are equivalent in order using en-US culture"); - if (compareOrdinal < 0) - Console.WriteLine($"<{one}> is less than <{two}> using ordinal comparison"); - else if (compareOrdinal > 0) - Console.WriteLine($"<{one}> is greater than <{two}> using ordinal comparison"); - else - Console.WriteLine($"<{one}> and <{two}> are equivalent in order using ordinal comparison"); - } - // - } - - private static void SortArrayOfStrings() - { - // - string[] lines = new string[] - { - @"c:\public\textfile.txt", - @"c:\public\textFile.TXT", - @"c:\public\Text.txt", - @"c:\public\testfile2.txt" - }; - - Console.WriteLine("Non-sorted order:"); - foreach (string s in lines) - { - Console.WriteLine($" {s}"); - } - - Console.WriteLine("\n\rSorted order:"); - - // Specify Ordinal to demonstrate the different behavior. - Array.Sort(lines, StringComparer.CurrentCulture); - - foreach (string s in lines) - { - Console.WriteLine($" {s}"); - } - // - } - - private static void SearchSortedArray() - { - // - string[] lines = new string[] - { - @"c:\public\textfile.txt", - @"c:\public\textFile.TXT", - @"c:\public\Text.txt", - @"c:\public\testfile2.txt" - }; - Array.Sort(lines, StringComparer.CurrentCulture); - - string searchString = @"c:\public\TEXTFILE.TXT"; - Console.WriteLine($"Binary search for <{searchString}>"); - int result = Array.BinarySearch(lines, searchString, StringComparer.CurrentCulture); - ShowWhere(lines, result); - - Console.WriteLine($"{(result > 0 ? "Found" : "Did not find")} {searchString}"); - - void ShowWhere(T[] array, int index) - { - if (index < 0) - { - index = ~index; - - Console.Write("Not found. Sorts between: "); - - if (index == 0) - Console.Write("beginning of sequence and "); - else - Console.Write($"{array[index - 1]} and "); - - if (index == array.Length) - Console.WriteLine("end of sequence."); - else - Console.WriteLine($"{array[index]}."); - } - else - { - Console.WriteLine($"Found at index {index}."); - } - } - // - } - - private static void SortListOfStrings() - { - // - List lines = new List - { - @"c:\public\textfile.txt", - @"c:\public\textFile.TXT", - @"c:\public\Text.txt", - @"c:\public\testfile2.txt" - }; - - Console.WriteLine("Non-sorted order:"); - foreach (string s in lines) - { - Console.WriteLine($" {s}"); - } - - Console.WriteLine("\n\rSorted order:"); - - lines.Sort((left, right) => left.CompareTo(right)); - foreach (string s in lines) - { - Console.WriteLine($" {s}"); - } - // - } - - private static void SearchSortedList() - { - // - List lines = new List - { - @"c:\public\textfile.txt", - @"c:\public\textFile.TXT", - @"c:\public\Text.txt", - @"c:\public\testfile2.txt" - }; - lines.Sort((left, right) => left.CompareTo(right)); - - string searchString = @"c:\public\TEXTFILE.TXT"; - Console.WriteLine($"Binary search for <{searchString}>"); - int result = lines.BinarySearch(searchString); - ShowWhere(lines, result); - - Console.WriteLine($"{(result > 0 ? "Found" : "Did not find")} {searchString}"); - - void ShowWhere(IList collection, int index) - { - if (index < 0) - { - index = ~index; - - Console.Write("Not found. Sorts between: "); - - if (index == 0) - Console.Write("beginning of sequence and "); - else - Console.Write($"{collection[index - 1]} and "); - - if (index == collection.Count) - Console.WriteLine("end of sequence."); - else - Console.WriteLine($"{collection[index]}."); - } - else - { - Console.WriteLine($"Found at index {index}."); - } - } - // - } - } -} diff --git a/samples/snippets/csharp/how-to/strings/Concatenate.cs b/samples/snippets/csharp/how-to/strings/Concatenate.cs deleted file mode 100644 index 4109fa5ec682d..0000000000000 --- a/samples/snippets/csharp/how-to/strings/Concatenate.cs +++ /dev/null @@ -1,104 +0,0 @@ -using System; -using System.Linq; -using System.Collections.Generic; -using System.Text; - -namespace HowToStrings -{ - static public class Concatenate - { - public static void Examples() - { - UsingAddWithConstantStrings(); - UsingAddWithVariables(); - UsingInterpolationWithVariables(); - UsingStringBuilder(); - UsingConcatAndJoin(); - UsingAggregate(); - } - - private static void UsingAddWithConstantStrings() - { - // - // Concatenation of literals is performed at compile time, not run time. - string text = "Historically, the world of data and the world of objects " + - "have not been well integrated. Programmers work in C# or Visual Basic " + - "and also in SQL or XQuery. On the one side are concepts such as classes, " + - "objects, fields, inheritance, and .NET Framework APIs. On the other side " + - "are tables, columns, rows, nodes, and separate languages for dealing with " + - "them. Data types often require translation between the two worlds; there are " + - "different standard functions. Because the object world has no notion of query, a " + - "query can only be represented as a string without compile-time type checking or " + - "IntelliSense support in the IDE. Transferring data from SQL tables or XML trees to " + - "objects in memory is often tedious and error-prone."; - - System.Console.WriteLine(text); - // - } - - private static void UsingAddWithVariables() - { - // - string userName = ""; - string dateString = DateTime.Today.ToShortDateString(); - - // Use the + and += operators for one-time concatenations. - string str = "Hello " + userName + ". Today is " + dateString + "."; - System.Console.WriteLine(str); - - str += " How are you today?"; - System.Console.WriteLine(str); - // - } - private static void UsingInterpolationWithVariables() - { - // - string userName = ""; - string date = DateTime.Today.ToShortDateString(); - - // Use string interpolation to concatenate strings. - string str = $"Hello {userName}. Today is {date}."; - System.Console.WriteLine(str); - - str = $"{str} How are you today?"; - System.Console.WriteLine(str); - // - } - - private static void UsingStringBuilder() - { - // - // Use StringBuilder for concatenation in tight loops. - var sb = new System.Text.StringBuilder(); - for (int i = 0; i < 20; i++) - { - sb.AppendLine(i.ToString()); - } - System.Console.WriteLine(sb.ToString()); - // - } - - private static void UsingConcatAndJoin() - { - // - string[] words = { "The", "quick", "brown", "fox", "jumps", "over", "the", "lazy", "dog." }; - - var unreadablePhrase = string.Concat(words); - System.Console.WriteLine(unreadablePhrase); - - var readablePhrase = string.Join(" ", words); - System.Console.WriteLine(readablePhrase); - // - } - - private static void UsingAggregate() - { - // - string[] words = { "The", "quick", "brown", "fox", "jumps", "over", "the", "lazy", "dog." }; - - var phrase = words.Aggregate((partialPhrase, word) =>$"{partialPhrase} {word}"); - System.Console.WriteLine(phrase); - // - } - } -} diff --git a/samples/snippets/csharp/how-to/strings/ModifyStrings.cs b/samples/snippets/csharp/how-to/strings/ModifyStrings.cs deleted file mode 100644 index c68703986b2d4..0000000000000 --- a/samples/snippets/csharp/how-to/strings/ModifyStrings.cs +++ /dev/null @@ -1,149 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Text; -using System.Text.RegularExpressions; - -namespace HowToStrings -{ - public static class ModifyStrings - { - public static void Examples() - { - ReplaceCreatesNewString(); - ReplaceChars(); - TrimWhitespace(); - RemoveText(); - ReplaceWithRegEx(); - ReplaceCharArray(); - } - - private static void ReplaceCreatesNewString() - { - // - string source = "The mountains are behind the clouds today."; - - // Replace one substring with another with String.Replace. - // Only exact matches are supported. - var replacement = source.Replace("mountains", "peaks"); - Console.WriteLine($"The source string is <{source}>"); - Console.WriteLine($"The updated string is <{replacement}>"); - // - } - - private static void ReplaceChars() - { - // - string source = "The mountains are behind the clouds today."; - - // Replace all occurrences of one char with another. - var replacement = source.Replace(' ', '_'); - Console.WriteLine(source); - Console.WriteLine(replacement); - // - } - - private static void TrimWhitespace() - { - // - // Remove trailing and leading white space. - string source = " I'm wider than I need to be. "; - // Store the results in a new string variable. - var trimmedResult = source.Trim(); - var trimLeading = source.TrimStart(); - var trimTrailing = source.TrimEnd(); - Console.WriteLine($"<{source}>"); - Console.WriteLine($"<{trimmedResult}>"); - Console.WriteLine($"<{trimLeading}>"); - Console.WriteLine($"<{trimTrailing}>"); - // - } - - private static void RemoveText() - { - // - string source = "Many mountains are behind many clouds today."; - // Remove a substring from the middle of the string. - string toRemove = "many "; - string result = string.Empty; - int i = source.IndexOf(toRemove); - if (i >= 0) - { - result= source.Remove(i, toRemove.Length); - } - Console.WriteLine(source); - Console.WriteLine(result); - // - } - - private static void ReplaceWithRegEx() - { - // - string source = "The mountains are still there behind the clouds today."; - - // Use Regex.Replace for more flexibility. - // Replace "the" or "The" with "many" or "Many". - // using System.Text.RegularExpressions - string replaceWith = "many "; - source = System.Text.RegularExpressions.Regex.Replace(source, "the\\s", LocalReplaceMatchCase, - System.Text.RegularExpressions.RegexOptions.IgnoreCase); - Console.WriteLine(source); - - string LocalReplaceMatchCase(System.Text.RegularExpressions.Match matchExpression) - { - // Test whether the match is capitalized - if (Char.IsUpper(matchExpression.Value[0])) - { - // Capitalize the replacement string - System.Text.StringBuilder replacementBuilder = new System.Text.StringBuilder(replaceWith); - replacementBuilder[0] = Char.ToUpper(replacementBuilder[0]); - return replacementBuilder.ToString(); - } - else - { - return replaceWith; - } - } - // - } - - private static void ReplaceCharArray() - { - // - string phrase = "The quick brown fox jumps over the fence"; - Console.WriteLine(phrase); - - char[] phraseAsChars = phrase.ToCharArray(); - int animalIndex = phrase.IndexOf("fox"); - if (animalIndex != -1) - { - phraseAsChars[animalIndex++] = 'c'; - phraseAsChars[animalIndex++] = 'a'; - phraseAsChars[animalIndex] = 't'; - } - - string updatedPhrase = new string(phraseAsChars); - Console.WriteLine(updatedPhrase); - // - } - - private static void UsingStringCreate() - { - // - // constructing a string from a char array, prefix it with some additional characters - char[] chars = { 'a', 'b', 'c', 'd', '\0' }; - int length = chars.Length + 2; - string result = string.Create(length, chars, (Span strContent, char[] charArray) => - { - strContent[0] = '0'; - strContent[1] = '1'; - for (int i = 0; i < charArray.Length; i++) - { - strContent[i + 2] = charArray[i]; - } - }); - - Console.WriteLine(result); - // - } - } -} diff --git a/samples/snippets/csharp/how-to/strings/ParseStringsUsingSplit.cs b/samples/snippets/csharp/how-to/strings/ParseStringsUsingSplit.cs deleted file mode 100644 index bae0d26ca0b2c..0000000000000 --- a/samples/snippets/csharp/how-to/strings/ParseStringsUsingSplit.cs +++ /dev/null @@ -1,145 +0,0 @@ -using System; -using System.Linq; - -namespace HowToStrings -{ - public static class ParseStringsUsingSplit - { - public static void Examples() - { - Console.WriteLine("Split words"); - Console.WriteLine(); - SplitWords(); - - Console.WriteLine("Enumerate words"); - Console.WriteLine(); - EnumerateWords(); - - Console.WriteLine("Split words with repeated separators"); - Console.WriteLine(); - SplitWordsWithRepeatedSeparators(); - - Console.WriteLine("Split on multiple chars"); - Console.WriteLine(); - SplitOnMultipleChars(); - - Console.WriteLine("Split on multiple chars with gaps"); - Console.WriteLine(); - SplitOnMultipleCharsWithGaps(); - - Console.WriteLine("Split using strings"); - Console.WriteLine(); - SplitUsingStrings(); - - Console.WriteLine("Split into no more than four substrings"); - Console.WriteLine(); - SplitFourTimes(); - } - - private static void SplitWords() - { - // - string phrase = "The quick brown fox jumps over the lazy dog."; - string[] words = phrase.Split(' '); - - foreach (var word in words) - { - System.Console.WriteLine($"<{word}>"); - } - // - } - - private static void EnumerateWords() - { - // - string phrase = "The quick brown fox jumps over the lazy dog."; - string[] words = phrase.Split(' '); - - for (int i = 0; i < words.Length; i++) - { - System.Console.WriteLine($"Index {i}: <{words[i]}>"); - } - // - } - - private static void SplitWordsWithRepeatedSeparators() - { - // - string phrase = "The quick brown fox jumps over the lazy dog."; - string[] words = phrase.Split(' '); - - foreach (var word in words) - { - System.Console.WriteLine($"<{word}>"); - } - // - } - - private static void SplitOnMultipleChars() - { - // - char[] delimiterChars = { ' ', ',', '.', ':', '\t' }; - - string text = "one\ttwo three:four,five six seven"; - System.Console.WriteLine($"Original text: '{text}'"); - - string[] words = text.Split(delimiterChars); - System.Console.WriteLine($"{words.Length} words in text:"); - - foreach (var word in words) - { - System.Console.WriteLine($"<{word}>"); - } - // - } - - private static void SplitOnMultipleCharsWithGaps() - { - // - char[] delimiterChars = { ' ', ',', '.', ':', '\t' }; - - string text = "one\ttwo :,five six seven"; - System.Console.WriteLine($"Original text: '{text}'"); - - string[] words = text.Split(delimiterChars); - System.Console.WriteLine($"{words.Length} words in text:"); - - foreach (var word in words) - { - System.Console.WriteLine($"<{word}>"); - } - // - } - - private static void SplitUsingStrings() - { - // - string[] separatingStrings = { "<<", "..." }; - - string text = "one< - } - - private static void SplitFourTimes() - { - // - string phrase = "The quick brown fox jumps over the lazy dog."; - string[] words = phrase.Split(' ', 4, System.StringSplitOptions.None); - - foreach (var word in words) - { - System.Console.WriteLine($"<{word}>"); - } - // - } - } -} diff --git a/samples/snippets/csharp/how-to/strings/Program.cs b/samples/snippets/csharp/how-to/strings/Program.cs deleted file mode 100644 index 04110e3a95676..0000000000000 --- a/samples/snippets/csharp/how-to/strings/Program.cs +++ /dev/null @@ -1,26 +0,0 @@ -using System; - -namespace HowToStrings -{ - class Program - { - static void Main(string[] args) - { - Console.WriteLine("============================ String.Split examples ================================================="); - Console.WriteLine(); - ParseStringsUsingSplit.Examples(); - Console.WriteLine("============================ String concatenation examples ================================================="); - Console.WriteLine(); - Concatenate.Examples(); - Console.WriteLine("============================ String Searching examples ================================================="); - Console.WriteLine(); - SearchStrings.Examples(); - Console.WriteLine("============================ Modify string examples ================================================="); - Console.WriteLine(); - ModifyStrings.Examples(); - Console.WriteLine("============================ Compare string examples ================================================="); - Console.WriteLine(); - CompareStrings.Examples(); - } - } -} diff --git a/samples/snippets/csharp/how-to/strings/SearchStrings.cs b/samples/snippets/csharp/how-to/strings/SearchStrings.cs deleted file mode 100644 index e94860e0e89ee..0000000000000 --- a/samples/snippets/csharp/how-to/strings/SearchStrings.cs +++ /dev/null @@ -1,121 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Text; - -namespace HowToStrings -{ - public class SearchStrings - { - public static void Examples() - { - SearchWithMethods(); - SearchByIndex(); - RegularExpressionsOne(); - RegularExpressionsValidation(); - } - - private static void SearchWithMethods() - { - // - string factMessage = "Extension methods have all the capabilities of regular static methods."; - - // Write the string and include the quotation marks. - Console.WriteLine($"\"{factMessage}\""); - - // Simple comparisons are always case sensitive! - bool containsSearchResult = factMessage.Contains("extension"); - Console.WriteLine($"Contains \"extension\"? {containsSearchResult}"); - - // For user input and strings that will be displayed to the end user, - // use the StringComparison parameter on methods that have it to specify how to match strings. - bool ignoreCaseSearchResult = factMessage.StartsWith("extension", System.StringComparison.CurrentCultureIgnoreCase); - Console.WriteLine($"Starts with \"extension\"? {ignoreCaseSearchResult} (ignoring case)"); - - bool endsWithSearchResult = factMessage.EndsWith(".", System.StringComparison.CurrentCultureIgnoreCase); - Console.WriteLine($"Ends with '.'? {endsWithSearchResult}"); - // - } - - private static void SearchByIndex() - { - // - string factMessage = "Extension methods have all the capabilities of regular static methods."; - - // Write the string and include the quotation marks. - Console.WriteLine($"\"{factMessage}\""); - - // This search returns the substring between two strings, so - // the first index is moved to the character just after the first string. - int first = factMessage.IndexOf("methods") + "methods".Length; - int last = factMessage.LastIndexOf("methods"); - string str2 = factMessage.Substring(first, last - first); - Console.WriteLine($"Substring between \"methods\" and \"methods\": '{str2}'"); - // - } - - private static void RegularExpressionsOne() - { - // - string[] sentences = - { - "Put the water over there.", - "They're quite thirsty.", - "Their water bottles broke." - }; - - string sPattern = "the(ir)?\\s"; - - foreach (string s in sentences) - { - Console.Write($"{s,24}"); - - if (System.Text.RegularExpressions.Regex.IsMatch(s, sPattern, System.Text.RegularExpressions.RegexOptions.IgnoreCase)) - { - Console.WriteLine($" (match for '{sPattern}' found)"); - } - else - { - Console.WriteLine(); - } - } - // - } - - private static void RegularExpressionsValidation() - { - // - string[] numbers = - { - "123-555-0190", - "444-234-22450", - "690-555-0178", - "146-893-232", - "146-555-0122", - "4007-555-0111", - "407-555-0111", - "407-2-5555", - "407-555-8974", - "407-2ab-5555", - "690-555-8148", - "146-893-232-" - }; - - string sPattern = "^\\d{3}-\\d{3}-\\d{4}$"; - - foreach (string s in numbers) - { - Console.Write($"{s,14}"); - - if (System.Text.RegularExpressions.Regex.IsMatch(s, sPattern)) - { - Console.WriteLine(" - valid"); - } - else - { - Console.WriteLine(" - invalid"); - } - } - // - } - } -}