Galileo Computing <openbook>
Galileo Computing - Programming the Net
Galileo Computing - Programming the Net


C# von Eric Gunnerson
Die neue Sprache für Microsofts .NET-Plattform
C# - Zum Katalog
gp Kapitel 18 Zeichenfolgen
  gp 18.1 Operationen
  gp 18.2 Konvertieren von Objekten in Zeichenfolgen
  gp 18.3 Ein Beispiel
  gp 18.4 StringBuilder
  gp 18.5 Reguläre Ausdrücke

Kapitel 18 Zeichenfolgen

Alle Zeichenfolgen in C# stellen Instanzen des System.String-Typs der Common Language Runtime dar. Aufgrund dieser Tatsache stehen viele integrierte Operationen für Zeichenfolgen zur Verfügung. Die String-Klasse definiert beispielsweise eine Indizierungsfunktion, mit der die Zeichen einer Zeichenfolge durchlaufen werden können:

using System;
class Test
{
    public static void Main()
    {
        string s = "Test String";

        for (int index = 0; index < s.Length; index++)
            Console.WriteLine("Char: {0}", s[index]);
    }
}

Galileo Computing

18.1 Operationen  downtop

Die string-Klasse ist ein Beispiel für einen unveränderlichen Typ, d. h., die in der Zeichenfolge enthaltenen Zeichen können von den Benutzern der Zeichenfolge nicht geändert werden. Alle Operationen, die von der Zeichenfolgenklasse ausgeführt werden, geben eine geänderte Version der Zeichenfolge zurück, anstatt die Instanz zu bearbeiten, für die die Methode aufgerufen wird.

Die String-Klasse unterstützt die folgenden Vergleichs- und Suchfunktionen:

Element Beschreibung
Compare() Vergleich zweier Zeichenfolgen.
CompareOrdinal() Vergleich zweier Zeichenfolgenbereiche mit Hilfe eines Ordnungszahlvergleichs.
CompareTo() Vergleich der aktuellen Instanz mit einer anderen Instanz.
EndsWith() Ermittelt, ob am Ende einer Zeichenfolge eine Teilzeichenfolge vorhanden ist.
StartsWith() Ermittelt, ob am Anfang einer Zeichenfolge eine Teilzeichenfolge vorhanden ist.
IndexOf() Gibt die Position des ersten Auftretens einer Teilzeichenfolge zurück.
LastIndexOf() Gibt die Position des letzten Auftretens einer Teilzeichenfolge zurück.

Die String-Klasse unterstützt die folgenden Bearbeitungsmethoden:

Element Beschreibung
Concat() Verkettet zwei oder mehr Zeichenfolgen miteinander. Werden Objekte übergeben, wird für diese die Funktion ToString() aufgerufen.
CopyTo() Kopiert eine bestimmte Anzahl Zeichen von einer Position in der Zeichenfolge in ein Array.
Insert() Gibt eine neue Zeichenfolge zurück, bei der an einer bestimmten Position eine Teilzeichenfolge eingefügt wurde.
Join() Fügt ein Array aus Zeichenfolgen zusammen, bei dem zwischen den Arrayelementen ein Trennzeichen steht.
PadLeft() Richtet eine Zeichenfolge links im Feld aus.
PadRight() Richtet eine Zeichenfolge rechts im Feld aus.
Remove() Löscht Zeichen aus einer Zeichenfolge.
Replace() Ersetzt alle Instanzen eines Zeichens durch ein anderes Zeichen.
Split() Erstellt ein Array aus Zeichenfolgen, indem eine Zeichenfolge bei jedem Auftreten mindestens eines Zeichens getrennt wird.
Substrng() Extrahiert eine Teilzeichenfolge aus einer Zeichenfolge.
ToLower() Gibt eine Zeichenfolge in Kleinschreibung zurück.
ToUpper() Gibt eine Zeichenfolge in Großschreibung zurück.
Trim() Entfernt Leerbereiche aus einer Zeichenfolge.
TrimEnd() Entfernt eine Zeichenkette am Ende einer Zeichenfolge.
TrimStart() Entfernt eine Zeichenkette am Anfang einer Zeichenfolge.


Galileo Computing

18.2 Konvertieren von Objekten in Zeichenfolgen  downtop

Die Funktion object.ToString() wird durch die integrierten Typen außer Kraft gesetzt, damit ein Wert leicht in eine Zeichenfolgendarstellung dieses Wertes konvertiert werden kann. Das Aufrufen von ToString() führt zu einer standardmäßigen Darstellung des Wertes; eine andere Darstellung kann durch den Aufruf von String.Format() erreicht werden. Weitere Informationen zu diesem Thema finden Sie im Abschnitt zur Formatierung in Kapitel 30, Überblick über die .NET-Frameworks.


Galileo Computing

18.3 Ein Beispiel  downtop

Die Teilungsfunktion kann dazu verwendet werden, eine Zeichenfolge in Teilzeichenfolgen mit Trennzeichen aufzuteilen.

using System;
class Test
{
    public static void Main()
    {
        string s = "Oh, I hadn't thought of that";
        char[] separators = new char[] {' ', ','};
        foreach (string sub in s.Split(separators))
        {
            Console.WriteLine("Word: {0}", sub);
        }
    }
}

Dieser Code erzeugt die folgende Ausgabe:

Word: Oh
Word:
Word: I
Word: hadn't
Word: thought
Word: of
Word: that

Das separators-Zeichenarray definiert, in welche Zeichen die Zeichenfolge aufgeteilt wird. Die Split()-Funktion gibt ein Array aus Zeichenfolgen zurück, die foreach-Anweisung durchläuft das Array und erzeugt die Ausgabe.

In diesem Fall ist die Ausgabe nicht besonders nützlich, da das Kommazeichen zweimal geteilt wird. Dies kann mit Hilfe der herkömmlichen Ausdrucksklassen behoben werden.


Galileo Computing

18.4 StringBuilder  downtop

Obwohl mit der Funktion String.Format() eine Zeichenfolge erstellt werden kann, die auf den Werten anderer Zeichenfolgen basiert, ist dies nicht die effizienteste Methode zur Zusammenfügung von Zeichenfolgen. Die Laufzeitumgebung stellt die Klasse StringBuilder bereit, mit der dieser Vorgang vereinfacht werden kann.

Die StringBuilder-Klasse unterstützt die folgenden Eigenschaften und Methoden:

Eigenschaft Beschreibung
Capacity Legt die Anzahl der Zeichen fest, die StringBuilder enthalten kann bzw. ruft diese ab.
[] Der Indizierer StringBuilder wird dazu verwendet, ein Zeichen an einer bestimmten Position zu setzen oder abzurufen.
Length Legt die Länge fest bzw. ruft diese ab.
MaxCapacity Ruft die maximale Kapazität von StringBuilder ab.
Methode Beschreibung
Append() Hängt die Zeichenfolgendarstellung eines Objekts an.
AppendFormat() Hängt unter Verwendung einer spezifischen Formatzeichenfolge eine Zeichenfolgendarstellung eines Objekts an.
EnsureCapacity() Stellt sicher, dass StringBuilder über genügend Platz für eine bestimmte Zeichenzahl verfügt.
Insert() Fügt die Zeichenfolgendarstellung eines angegebenen Objekts an einer spezifischen Position ein.
Remove() Entfernt die angegebenen Zeichen.
Replace() Ersetzt alle Instanzen eines Zeichens durch ein neues Zeichen.

Das folgende Beispiel verdeutlicht, wie die Klasse StringBuilder zum Erstellen einer Zeichenfolge aus separaten Zeichenfolgen eingesetzt werden kann.

using System;
using System.Text;
class Test
{
    public static void Main()
    {
    string s = "I will not buy this record, it is scratched";
        char[] separators = new char[] {' ', ','};
        StringBuilder sb = new StringBuilder();
        int number = 1;
        foreach (string sub in s.Split(separators))
        {
            sb.AppendFormat("{0}: {1} ", number++, sub);
        }
        Console.WriteLine("{0}", sb);
    }
}

Dieser Code erzeugt eine Zeichenfolge mit nummerierten Wörtern und führt zur folgenden Ausgabe:

1: I 2: will 3: not 4: buy 5: this 6: record 7: 
 8: it 9: is 10: scratched

Da über den Aufruf von split() sowohl das Leerzeichen als auch das Komma als Trennzeichen festgelegt wird, wird angenommen, dass sich zwischen Komma und Leerzeichen ein Wort befindet, was zu einem leeren Eintrag führt.


Galileo Computing

18.5 Reguläre Ausdrücke  downtop

Wenn die Suchfunktionen der String-Klasse nicht ausreichen, kann auf die reguläre Ausdrucksklasse Regex des System.Text-Namespace zurückgegriffen werden. Reguläre Ausdrücke bieten eine sehr leistungsfähige Methode für Such- und/oder Ersetzungsläufe.

Obwohl an dieser Stelle einige Beispiele zur Verwendung dieser regulären Ausdrücke aufgezeigt werden sollen, kann im Rahmen dieses Buches keine erschöpfende Beschreibung gegeben werden. Zu diesem Thema stehen verschiedene Bücher zur Verfügung, darüber hinaus wird dieser Themenbereich in nahezu jedem Perl-Handbuch behandelt.

Die regulären Ausdrucksklassen sind eine recht interessante Methode zur Leistungsoptimierung. Statt den regulären Ausdruck für jede Übereinstimmung zu interpretieren, wird ein kurzes Programm zur Implementierung der Übereinstimmung des regulären Ausdrucks geschrieben, und dieser Code wird erneut ausgeführt.

Das vorstehende Beispiel mit Verwendung von Split() kann mit Hilfe eines regulären Ausdrucks umgeschrieben werden. Hierbei wird nicht über einzelne Zeichen festgelegt, wie die Teilung erfolgen soll, sondern über den regulären Ausdruck. Auf diese Weise wird der leere Eintrag des vorangegangenen Beispiels entfernt.

// Datei: regex.cs
// Kompilieren mit: csc /r:system.text.regularexpressions.dll regex.cs
using System;
using System.Text.RegularExpressions;
class Test
{
    public static void Main()
    {
        string s = "Oh, I hadn't thought of that";
        Regex regex = new Regex(@"( |, )");
        char[] separators = new char[] {' ', ','};
        foreach (string sub in regex.Split(s))
        {
            Console.WriteLine("Word: {0}", sub);
        }
    }
}

Dieser Code erzeugt die folgende Ausgabe:

Word: Oh
Word: I
Word: hadn't
Word: thought
Word: of
Word: that

Im regulären Ausdruck wird die Zeichenfolge entweder basierend auf einem Leerzeichen oder einem Komma mit nachfolgendem Leerzeichen geteilt.


Galileo Computing

18.5.1 Komplexere Syntaxanalyse  toptop

Der Einsatz regulärer Ausdrücke zur Verbesserung der Funktion von Split() demonstriert nicht wirklich deren Leistungsfähigkeit. Im folgenden Beispiel werden reguläre Ausdrücke zur Syntaxanalyse einer IIS-Protokolldatei verwendet. Die Protokolldatei sieht in etwa so aus:

#Software: Microsoft Internet Information Server 
4.0
#Version: 1.0
#Date: 1999-12-31 00:01:22
#Fields: time c-ip cs-method cs-uri-stem sc-status
00:01:31 157.56.214.169 GET /Default.htm 304
00:02:55 157.56.214.169 GET /docs/project/overview.htm 200

Der folgende Code setzt dies in eine etwas brauchbarere Form um.

// Datei: logparse.cs
// Kompilieren mit: csc logparse.cs /r:system.net.dll /r:system.text.regularexpressions.dll
using System;
using System.Net;
using System.IO;
using System.Text.RegularExpressions;
using System.Collections;

class Test
{
    public static void Main(string[] args)
    {
        if (args.Length  == 0) // Wir benötigen eine zu analysierende 
Datei
        {
            Console.WriteLine("No log file specified.");
        }
        else
            ParseLogFile(args[0]);
    }
    public static void ParseLogFile(string    filename)
    {
        if (!System.IO.File.FileExists(filename))
        {
            Console.WriteLine ("The file specified does not exist.");
        }
        else
        {
            FileStream f = new FileStream(filename, FileMode.Open);
            StreamReader stream = new StreamReader(f);

            string line;
            line = stream.ReadLine();    // Headerzeile
            line = stream.ReadLine();    // Versionszeile
            line = stream.ReadLine();    // Datumszeile

            Regex    regexDate= new Regex(@"\:\s(?<date>[^\s]+)\s");
            Match    match = regexDate.Match(line);
            string    date = "";
            if (match.Length != 0)
                date = match.Group("date").ToString();

            line = stream.ReadLine();    // Felderzeile

            Regex    regexLine =
                new Regex(        // Stellen oder : abgleichen
                        @"(?<time>(\d|\:)+)\s" +
                            // Stellen oder . abgleichen
                        @"(?<ip>(\d|\.)+)\s" 
+
                            // alle Nichtleerzeichen abgleichen
                        @"(?<method>\S+)\s" +
                            // alle Nichtleerzeichen abgleichen
                        @"(?<uri>\S+)\s" +
                            // alle Nichtleerzeichen abgleichen
                        @"(?<status>\d+)");

                // Zeilen lesen und
                // IISLogRow für jede Zeile hinzufügen
            while ((line = stream.ReadLine()) != null)
            {
                //Console.WriteLine(line);
                match = regexLine.Match(line);
                if (match.Length != 0)
                {
                    Console.WriteLine("date: {0} {1}", date,
                                      match.Group("time"));
                    Console.WriteLine("IP Address: {0}",
                                      match.Group("ip"));
                    Console.WriteLine("Method: {0}",
                                      match.Group("method"));
                    Console.WriteLine("Status: {0}",
                                      match.Group("status"));
                    Console.WriteLine("URI: {0}\n",
                                      match.Group("uri"));
                }
            }
            f.Close();
        }
    }
}

Die allgemeine Struktur des Codes sollte Ihnen bekannt vorkommen. In diesem Beispiel werden zwei reguläre Ausdrücke verwendet. Die Datumszeichenfolge und der reguläre Ausdruck zu deren Abgleich lauten:

#Date: 1999-12-31 00:01:22
\:\s(?<date>[^\s]+)\s

Im Code werden reguläre Ausdrücke üblicherweise unter Verwendung der wörtlichen Zeichenfolgensyntax geschrieben, da die reguläre Ausdruckssyntax auch umgekehrte Schrägstriche verwendet. Reguläre Ausdrücke können am besten gelesen werden, wenn sie in separate Elemente untergliedert werden. Das

\:

entspricht dem Doppelpunkt (:). Der umgekehrte Schrägstrich (\) ist erforderlich, da der Doppelpunkt eine andere Bedeutung aufweist. Das

\s

entspricht einem einzelnen Leerzeichen (Tabulator oder Leertaste). Im folgenden Abschnitt

(?<date>[^\s]+)

gibt ?<date> den abzugleichenden Wert an, der später extrahiert werden soll. Die Gruppe [^\s] wird als Zeichengruppe bezeichnet; das ^-Zeichen bedeutet hierbei »keines der folgenden Zeichen«. Diese Gruppe gleicht demnach alle Nichtleerzeichen ab. Des Weiteren werden mit dem +-Zeichen die Stellen abgeglichen, die der vorangegangenen Beschreibung (Nichtleerzeichen) entsprechen. Die Klammern legen fest, wie die extrahierte Zeichenfolge abgeglichen werden soll. Im vorangegangenen Beispiel wird dieser Teil des Ausdrucks mit 1999-12-31 abgeglichen.

Zum genaueren Abgleich hätte der /d-Bezeichner verwendet werden können. In diesem Fall lautet der ganze Ausdruck:

\:\s(?<date>\d\d\d\d-\d\d-\d\d)\s

Hiermit wird der einfache reguläre Ausdruck abgedeckt. Zum Abgleich jeder Protokolldateizeile wird ein komplexerer regulärer Ausdruck verwendet. Aufgrund der Regelmäßigkeit der Zeile hätte auch Split() verwendet werden können, aber diese Vorgehensweise wäre nicht so anschaulich. Die Klauseln des regulären Ausdrucks lauten:

(?<time>(\d|\:)+)\s      // Zum Extrahieren 
der Uhrzeit Stellen oder : abgleichen
(?<ip>(\d|\.)+)\s        // Zum Abrufen der IP-Adresse Stellen 
oder . abgleichen
(?<method>\S+)\s         // Alle Nichtleerzeichen für Methode
(?<uri>\S+)\s            // Alle Nichtleerzeichen für uri
(?<status>\d+)           // Alle Stellen für Status





1    Das Programm wird unter Verwendung der .NET-Zwischensprache geschrieben – der gleichen Sprache, mit der C# die Ausgabe einer Kompilierung erzeugt.

   

Select * from SQL Server 2000




Copyright © Galileo Press GmbH 2001 - 2002
Für Ihren privaten Gebrauch dürfen Sie die Online-Version natürlich ausdrucken. Ansonsten unterliegt das <openbook> denselben Bestimmungen, wie die gebundene Ausgabe: Das Werk einschließlich aller seiner Teile ist urheberrechtlich geschützt. Alle Rechte vorbehalten einschließlich der Vervielfältigung, Übersetzung, Mikroverfilmung sowie Einspeicherung und Verarbeitung in elektronischen Systemen.


[Galileo Computing]

Galileo Press GmbH, Gartenstraße 24, 53229 Bonn, fon: 0228.42150.0, fax 0228.42150.77, info@galileo-press.de