Kolumne: C# im Fokus

Zeichenketten mit char String und StringBuilder
Kommentare

Die .NET-Plattform hat sich in den letzten Jahren stetig weiter entwickelt. Gestartet mit der .NET-Version 1.0, ist nun die Version 4.0 verfügbar. Hinzugekommen sind dabei nicht nur neue und erweiterte Bibliotheken, sondern auch der Sprachumfang ist erweitert worden. Die neue C#-Kolumne, greift elementare Sprachfeatures auf und erläutert sie praktisch.

Jede Anwendung verwendet Zeichenkettenoperationen. Die Programmiersprache C# stellt insgesamt drei elementare Datentypen zur Verfügung, um Zeichen beziehungsweise Zeichenketten ablegen zu können: String, char und StringBuilder. Welche davon schließlich verwendet wird, hängt von verschiedenen Faktoren ab.

Der Datentyp char stellt den kleinsten der Typen dar und kann genau ein Zeichen aufnehmen. Wird dieser Typ als Array deklariert, kann darüber eine Zeichenkette abgebildet werden. In C# ist das allerdings nicht notwendig, für diese Aufgabe ist bereits die Klasse String vordefiniert. In C# ist ein string ein Objekt der Klasse String, die eine schreibgeschützte char-Sequenz verwaltet. Das heißt, intern wird eine Zeichenkette als char-Array verwaltet. Im Gegensatz zu anderen Sprachen verwendet C# jedoch kein bestimmtes Endzeichen (NULL-Terminating), um das Ende der Zeichenfolge zu markieren. Die Länge der Zeichenkette verwaltet die String-Klasse intern. Von außen kann die Eigenschaft Length verwendet werden, um die Länge abzurufen. Dabei gibt diese Eigenschaft immer die Anzahl der char-Einträge wieder und nicht die Anzahl der enthaltenen Unicode-Zeichen. Da der char-Datentyp genau ein Zeichen aufnehmen kann, ist er immer dann einzusetzen, wenn maximal ein Zeichen verarbeitet werden muss. Als Beispiel kann hier das Einlesen einer Tasteneingabe genannt werden. Müssen mehrere Zeichen, zum Beispiel aus einer Datei, eingelesen werden, eignet sich der string-Datentyp. Neben den string-Datentypen existiert allerdings noch der StringBuilder, der im weiteren Verlauf noch erläutert wird.

Die Klasse „String“

Der Datentyp string gehört mit zu den am häufigsten verwendeten Datentypen. Auch wenn die Verwendung relativ einfach ist, sollten einige Punkte beachtet werden: Bei string handelt es sich zunächst einmal um einen so genannten unveränderlichen Datentypen. Das heißt, eine direkte Änderung der intern gespeicherten Zeichenkette ist nicht möglich. Alle Operationen, zum Beispiel String.Replace oder auch String.Remove, geben eine neue Zeichenkette zurück. Somit wird bei jeder Manipulation neuer Speicherplatz angefordert. Stehen viele dieser Operationen an, eignet sich dafür eher die StringBuilder-Klasse. Um den Speicherverbrauch zu optimieren, speichert C# identische Zeichenketten nur einmal und verweist auf diese. Dieses Verhalten wird als String Interning bezeichnet. Listing 1 zeigt dazu ein Beispiel.

Listing 1

private static void Example1()
{
    string strA = "Zeichenkette 1"; 
    string strB = "Zeichenkette 1";
    if (String.ReferenceEquals(strA, strB))
        Console.WriteLine("TRUE");
    strB = String.Copy("Zeichenkette 1");
    if (String.ReferenceEquals(strA, strB))
        Console.WriteLine("TRUE");
}  

Den Variablen strA und strB werden jeweils die gleichen Zeichenketten zugewiesen. Der anschließende Vergleich der Speicherstellen zeigt, dass beide Variablen auf dieselbe Speicherstelle verweisen. Um diesen internen Mechanismus zu umgehen, kann per Copy-Methode die Anlage einer neuen unabhängigen Variablen erzwungen werden, wie ebenfalls in Listing 1 zu sehen ist. Der erste konditionale Block liefert TRUE zurück, das heißt, die beiden Zeichenketten verweisen auf die gleiche Speicherstelle. Die zweite Abfrage hingegen endet mit FALSE und zeigt somit, dass per Copy eine neue Speicherstelle für die Zeichenkette erstellt wurde. Der Vergleich der Speicherstellen ist zwar teilweise nützlich, in der Regel geht es aber darum, die Inhalte der Zeichenketten zu vergleichen. Im einfachsten Fall kann dazu der Operator == verwendet werden. Er führt einen exakten Vergleich aus. Unterscheidet sich die Zeichenkette nur in der Groß- und Kleinschreibung, endet der Vergleich negativ. Die .NET-Klasse String bietet aber weitaus intelligentere Vergleichsmechanismen. Listing 2 zeigt dazu einige Beispiele und die Abbildung 1 die dazu gelieferten Vergleichsergebnisse. Die beiden definierten Zeichenketten strA und strB sagen inhaltlich dasselbe aus, unterscheiden sich jedoch in der Schreibweise erheblich. Der einfache Vergleich mittels ==-Operation liefert daher auch FALSE.

Listing 2

private static void Example2()
{
    string strA = "Peter S. ging schließlich zu Fuß spazieren";
    string strB = "Peter s. ging schliesslich zu Fuss spazieren";
    Console.WriteLine("Methode equals ergibt: {0}", strA==strB);
    Console.WriteLine("Methode equals ergibt: {0}", strA.Equals(strB));
    Console.WriteLine("Methode equals ergibt: {0}", 
          strA.Equals(strB,StringComparison.CurrentCultureIgnoreCase));
    Console.WriteLine("Methode equals ergibt: {0}", 
          String.Compare(strA, strB)==0);
    Console.WriteLine("Methode equals ergibt: {0}", 
          String.Compare(strA, strB, true) == 0);
    Console.WriteLine("Methode equals ergibt: {0}", String.Compare(strA, strB, 
          StringComparison.CurrentCultureIgnoreCase) == 0);
    Console.WriteLine("Methode equals ergibt: {0}", strA.CompareTo(strB) == 0);
}  

Abb. 1: Ergebnisse der verschiedenen Zeichenkettenvergleiche
Abb. 1: Ergebnisse der verschiedenen Zeichenkettenvergleiche

Eine bessere Vergleichsmöglichkeit bietet die Methode equals. Wie zu sehen ist, liefert der einfache Einsatz der Methode das gleiche Ergebnis wie zuvor der ==-Operator. Wird der Methode jedoch eine Länderinformation mitgegeben, endet der Zeichenkettenvergleich positiv. Die umfangreichsten Optionen für den Zeichenkettenvergleich besitzt die statische Methode String.Compare. Ohne Aufruf jeglicher Länderinformationen liefert diese zunächst auch FALSE zurück. Beim zweiten Aufruf der Methode wird über den dritten Parameter die Berücksichtigung der Groß- und Kleinschreibung deaktiviert. Dadurch liefert die Methode ebenfalls das korrekte Vergleichsergebnis zurück. Die Compare-Methode besitzt noch eine Menge an weiteren Parameterüberladungen, um den Vergleich dediziert steuern zu können. Neben den bereits genannten existiert noch die CompareTo-Methode, die allerdings nicht für Zeichenkettenvergleiche verwendet werden sollte. Empfehlenswert ist immer die Verwendung der statischen Methode String.Compare. Hier muss nicht wie bei der Verwendung von equals selbst überprüft werden, ob eine verwendete string-Variable null ist. Um zu prüfen, ob eine Zeichenkette gesetzt ist, bietet sich die statische Methode String.IsNullOrEmpty an. Listing 3 zeigt den Einsatz der Methode.

Listing 3

private static void Example3()
{
    string strA = null;
    if (string.IsNullOrEmpty(strA))
        Console.WriteLine("strA ist nicht gesetzt.");
}  

Muss eine Zeichenkette mit identischen Zeichen erstellt werden, kann dafür der Konstruktor wie folgt aufgerufen werden: new String(‚X‘, 30) . Dadurch wird eine Zeichenkette erstellt, die 30 Mal den Buchstaben X enthält. Oft ist es notwendig, mehrere Zeichenketten zu einer Zeichenkette zusammenzuführen. Die einfachste Möglichkeit besteht dabei in der Verwendung des Operators +. Flexibler ist allerdings die Verwendung der Methode String.Format, da hierüber zusätzliche Formatierungen und Inhalte in die Zeichenkette eingebracht werden können. Auch kann die statische Methode String.Concat verwendet werden. Sie erstellt intern einen entsprechend großen Speicherbereich, um die Teilzeichenketten zusammenzuführen. Das bedeutet gegenüber der String.Format-Methode, die intern einen StringBuilder verwendet, eine bessere Performance. Die String-Klasse enthält neben den hier genannten Möglichkeiten viele weitere nützliche Methoden, ein Blick in die API-Beschreibung [1] lohnt sich daher.

Unsere Redaktion empfiehlt:

Relevante Beiträge

Meinungen zu diesem Beitrag

X
- Gib Deinen Standort ein -
- or -