Procedura: confrontare stringhe (Guida per programmatori C#)

 

Data di pubblicazione: aprile 2016

Per la documentazione più recente di Visual Studio 2017 RC, vedere documentazione di Visual Studio 2017 RC.

Quando si confrontano stringhe, si produce un risultato che indica che una stringa è maggiore o minore dell'altra o che le due stringhe sono uguali. Le regole in base alle quali viene determinato il risultato sono diverse a seconda che si esegua un confronto ordinale o un confronto dipendente dalle impostazioni cultura. È importante utilizzare il tipo corretto di confronto per l'attività specifica.

Utilizzare confronti ordinali di base quando è necessario confrontare o ordinare i valori di due stringhe indipendentemente dalle convenzioni linguistiche. Un confronto ordinale di base (System.StringComparison.Ordinal) distingue tra maiuscole e minuscole, cioè le due stringhe devono corrispondere esattamente carattere per carattere: "file" non equivale a "FILE" né a "File". Una variazione utilizzata frequentemente è System.StringComparison.OrdinalIgnoreCase, che consente la corrispondenza di "file", "File" e "FILE". StringComparison.OrdinalIgnoreCase viene spesso utilizzato per confrontare nomi di file, nomi di percorsi, percorsi di rete e qualsiasi altra stringa il cui valore non cambia in base alle impostazioni locali del computer dell'utente. Per ulteriori informazioni, vedere System.StringComparison.

I confronti dipendenti dalle impostazioni cultura sono utilizzati in genere per confrontare e ordinare stringhe che sono input di utenti finali, perché i caratteri e le convenzioni di ordinamento di queste stringhe possono variare a seconda delle impostazioni locali del computer dell'utente. Anche stringhe che contengono caratteri identici potrebbero essere ordinate in modo diverso a seconda delle impostazioni cultura del thread corrente.

System_CAPS_ICON_note.jpg Nota

Quando si confrontano stringhe, è opportuno utilizzare i metodi che specificano in modo esplicito il tipo di confronto che si intende eseguire. In questo modo il codice può essere gestito e letto con maggiore facilità. Quando possibile, utilizzare gli overload dei metodi delle classi System.String e System.Array che accettano un parametro di enumerazione StringComparison, in modo da poter specificare il tipo di confronto da eseguire. È meglio evitare l'utilizzo degli operatori == e != quando si confrontano stringhe. Inoltre, evitare di utilizzare i metodi di istanza String.CompareTo perché nessuno degli overload accetta StringComparison.

Nell'esempio seguente viene mostrato come confrontare correttamente stringhe i cui valori non cambiano in base alle impostazioni locali del computer dell'utente. Viene inoltre illustrata la funzionalità inserimento di stringa di C#. Quando un programma dichiara due o più variabili di stringa identiche, il compilatore le archivia tutte nella stessa posizione. Chiamando il metodo ReferenceEquals, è possibile vedere che le due stringhe si riferiscono allo stesso oggetto in memoria. Utilizzare il metodo String.Copy per evitare l'inserimento di stringa, come mostrato nell'esempio.


            // Internal strings that will never be localized.
            string root = @"C:\users";
            string root2 = @"C:\Users";

            // Use the overload of the Equals method that specifies a StringComparison.
            // Ordinal is the fastest way to compare two strings.
            bool result = root.Equals(root2, StringComparison.Ordinal);

            Console.WriteLine("Ordinal comparison: {0} and {1} are {2}", root, root2,
                                result ? "equal." : "not equal.");

            // To ignore case means "user" equals "User". This is the same as using
            // String.ToUpperInvariant on each string and then performing an ordinal comparison.
            result = root.Equals(root2, StringComparison.OrdinalIgnoreCase);
            Console.WriteLine("Ordinal ignore case: {0} and {1} are {2}", root, root2,
                                 result ? "equal." : "not equal.");

            // A static method is also available.
            bool areEqual = String.Equals(root, root2, StringComparison.Ordinal);


            // String interning. Are these really two distinct objects?
            string a = "The computer ate my source code.";
            string b = "The computer ate my source code.";

            // ReferenceEquals returns true if both objects
            // point to the same location in memory.
            if (String.ReferenceEquals(a, b))
                Console.WriteLine("a and b are interned.");
            else
                Console.WriteLine("a and b are not interned.");

            // Use String.Copy method to avoid interning.
            string c = String.Copy(a);

            if (String.ReferenceEquals(a, c))
                Console.WriteLine("a and c are interned.");
            else
                Console.WriteLine("a and c are not interned.");

            // Output:
            // Ordinal comparison: C:\users and C:\Users are not equal.
            // Ordinal ignore case: C:\users and C:\Users are equal.
            // a and b are interned.
            // a and c are not interned.

Nell'esempio seguente viene mostrato come confrontare stringhe nel modo preferito tramite i metodi System.String che accettano un'enumerazione StringComparison. Notare che i metodi di istanza String.CompareTo non vengono utilizzati qui, perché nessuno degli overload accetta StringComparison.

            // "They dance in the street."
            // Linguistically (in Windows), "ss" is equal to 
            // the German essetz: 'ß' character in both en-US and de-DE cultures.
            string first = "Sie tanzen in die Straße."; 
            string second = "Sie tanzen in die Strasse.";
            
            Console.WriteLine("First sentence is {0}", first);
            Console.WriteLine("Second sentence is {0}", second);

            // Store CultureInfo for the current culture. Note that the original culture
            // can be set and retrieved on the current thread object.
            System.Threading.Thread thread = System.Threading.Thread.CurrentThread;
            System.Globalization.CultureInfo originalCulture = thread.CurrentCulture;

            // Set the culture to en-US.
            thread.CurrentCulture = 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, StringComparison.CurrentCulture);
            Console.WriteLine("Comparing in {0} returns {1}.", originalCulture.Name, i);
           
            // Change the current culture to Deutch-Deutchland.
            thread.CurrentCulture = new System.Globalization.CultureInfo("de-DE");
            i = String.Compare(first, second, StringComparison.CurrentCulture);
            Console.WriteLine("Comparing in {0} returns {1}.", thread.CurrentCulture.Name, i);
            
            // For culture-sensitive string equality, use either StringCompare as above
            // or the String.Equals overload that takes a StringComparison value.
            thread.CurrentCulture = originalCulture;
            bool b = String.Equals(first, second, StringComparison.CurrentCulture);
            Console.WriteLine("The two strings {0} equal.", b == true ? "are" : "are not");

            /*
             * Output:
                First sentence is Sie tanzen in die Straße.
                Second sentence is Sie tanzen in die Strasse.
                Comparing in en-US returns 0.
                Comparing in de-DE returns 0.
                The two strings are equal.
             */

Nell'esempio seguente viene mostrato come ordinare e cercare stringhe in una matrice in modo dipendente dalle impostazioni cultura tramite i metodi Array statici che accettano un parametro System.StringComparer.

    class SortStringArrays
    {
        static void Main()
        {
            
            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("   {0}", s);
            }

            Console.WriteLine("\n\rSorted order:");

            // Specify Ordinal to demonstrate the different behavior.
            Array.Sort(lines, StringComparer.Ordinal);

            foreach (string s in lines)
            {
                Console.WriteLine("   {0}", s);
            }
           

            string searchString = @"c:\public\TEXTFILE.TXT";
            Console.WriteLine("Binary search for {0}", searchString);
            int result = Array.BinarySearch(lines, searchString, StringComparer.OrdinalIgnoreCase);
            ShowWhere<string>(lines, result);

            //Console.WriteLine("{0} {1}", result > 0 ? "Found" : "Did not find", searchString);

            // Keep the console window open in debug mode.
            System.Console.WriteLine("Press any key to exit.");
            System.Console.ReadKey();
        }

        // Displays where the string was found, or, if not found,
        // where it would have been located.
        private static void ShowWhere<T>(T[] array, int index)
        {
            if (index < 0)
            {
                // If the index is negative, it represents the bitwise
                // complement of the next larger element in the array.
                index = ~index;

                Console.Write("Not found. Sorts between: ");

                if (index == 0)
                    Console.Write("beginning of array and ");
                else
                    Console.Write("{0} and ", array[index - 1]);

                if (index == array.Length)
                    Console.WriteLine("end of array.");
                else
                    Console.WriteLine("{0}.", array[index]);
            }
            else
            {
                Console.WriteLine("Found at index {0}.", index);
            }
        }


    }
    /*
     * Output:
        Non-sorted order:
           c:\public\textfile.txt
           c:\public\textFile.TXT
           c:\public\Text.txt
           c:\public\testfile2.txt

        Sorted order:
           c:\public\Text.txt
           c:\public\testfile2.txt
           c:\public\textFile.TXT
           c:\public\textfile.txt
        Binary search for c:\public\TEXTFILE.TXT
        Found at index 2.
     */

Le classi di raccolte quali System.Collections.Hashtable, System.Collections.Generic.Dictionary<TKey, TValue> e System.Collections.Generic.List<T> dispongono di costruttori che accettano un parametro System.StringComparer quando il tipo degli elementi o delle chiavi è string. In generale, è consigliabile utilizzare questi costruttori quando possibile e specificare Ordinal o OrdinalIgnoreCase.

System.Globalization.CultureInfo
System.StringComparer
Stringhe
Confronto di stringhe
Globalizzazione e localizzazione di applicazioni

Mostra: