Programmieren in C - Zeiger, Zeichenketten und Dateien

in #programmieren6 years ago

 Bild Referenz:  https://commons.wikimedia.org/wiki/File:C_language_pointer_arithmetic_chars.png

Intro

  Hallo, ich bins @drifter2! Der heutige Artikel ist am englischen Artikel: "C Pointers, Strings and Files" basiert. Also werden wir vieles über Zeiger (Pointers), Zeichenketten (Strings) und Dateien (Files) lernen. Ihr werdet euch endlich verstehen was Zeiger sind und wie man die benutzt, viele nützliche Bibliothek Funktionen für Zeichenketten kennen-lernen und auch lernen wie man Dateien in C verarbeitet (öffnen, erstellen, lesen, schreiben und schließen). Um alles noch besser zu verstehen werden ich am Ende auch ein komplettes Beispielprogramm haben! Im Englischen Artikel habe ich ein paar nützliche Dinge erwähnt die wir heute in Deutsch übersetzen werden. Da ich aber nur die ganz "Basic" Sachen erklärt habe werden wir nach mehr Informationen zu jeden dieser drei Thematiken suchen. Natürlich empfehle ich euch wieder die vorherigen Artikel der Serie zu lesen, die ihr am Ende dieses Artikels finden könnt! Also dann jetzt geht's los!

Zeiger

    Wie der Name bereits vordeutet, "zeigen" diese an eine spezifische Speicheradresse Wir speichern also nicht direkt Informationen wie bei Variablen, aber nur die Adresse von einem gewissen Wert den wir verwenden wollen. Die Felder (Arrays) die wir letztes mal analysiert haben sind z. B. auch ein Zeiger der zu dem ersten Feld zeigen (Feld[0]). Sagen wir mal ein char-Feld ist bei der Adresse 12000 abgespeichert und wir wollen z. B. den Wert bei Index 5 benutzen. Dieser Wert wirt natürlich in der Adresse: 12000+5*sizeof(char) = 12005 sein, weil ein char in C 1 Byte groß ist. Das beschreibt wie die Sprache C, A[index] im Hintergrund ausführt. Also um alles mal ganz schnell zu zusammenzufassen: "Ein Zeiger repräsentiert eine Adresse und keinen Wert wie Variablen".

Definition 

Zur Definition eines Zeigers haben wir die folgende Formulierung:

Datentyp * Name;

    Das heißt das wir den Datentyp bestimmen zu dem gezeigt wird und auch einen Namen geben. Dazwischen kommt ein Asterisk ('*') der dem Compiler sagt das es sich an einen Zeiger handelt. Natürlich können wir auch mehrere Asterisk's haben, die meistens bei dynamischen Mehrdimensionalen Feldern benutzt werden (werden nächstes mal darüber reden) und die sogenannten Zeiger auf Zeiger bilden.

    Um einen Zeiger auf einer Ganzzahl zu deklarieren würden wir z. B. die folgende Linie Code schreiben:

int * zeiger;

Zuweisungen

Sagen wir mal wir haben den vorherigen Zeiger und die folgende Variabel:

int var = 0;

Wie wir sehen können ist "var" auf '0' initialisiert.

Damit der Zeiger an diese Variabel zeigt benutzen wir den folgenden Adressoperator:

zeiger = &var; 

Die generelle Formulierung ist:

Zeigername = & Variabelname;

    Wie ihr sehen könnt benutzen wir das Symbol '&' welches die Speicheradresse der "folgenden" Variabel ausgibt. Ein Zeiger ändert oder initialisiert dadurch die abgespeicherte Adresse.

    Wir könnten diesen Operator ('&') auch bei einer printf() Funktion benutzen um die eigentliche Adresse in der eine Variabel abgespeichert ist auszudrucken indem wir folgendes tun:

printf("%d", &var);

    Um an den Wert/Inhalt zuzugreifen wo ein Zeiger hinzeigt benutzen wir den sogenannten Inhaltsoperator. Dazu dient der Asterisk ('*') der auch bei der Definition von Zeigern benutzt wurde. Das heißt das wir an den abgespeicherten Wert wo der Zeiger hinzeigt mit dem folgenden Code zugreifen können:

printf("%d", *zeiger);

Das '0' ausdrucken würde da der Zeiger an "var" zeigt.

Was passiert wenn wir beide Operatoren benutzen? z. B.:

printf("%d", *&var);

     '&' hat eine höhere Priorität als '*' und deswegen wird '0' ausgedruckt. Wir greifen ja auf die Speicheradresse zu ('&') und danach lesen wir den Inhalt ('*'), genau wie wenn wir diese Operatoren nicht hätten.

Nullzeiger

    Wenn ein Zeiger an "nichts" zeigt dann sollte man den Wert NULL zuweisen. Das hilft uns beim Programmieren da durch der Zeigen ungültig ist und auch nicht sinnvoll genutzt werden kann. Bei NULL-Werten (Fehler) in der Laufzeit haben wir meistens einen Programmabbruch.

Sagen wir mal wir haben den vorherigen Zeiger. Initialisieren wir den mal zu NULL:

zeiger = NULL;

Jetzt können wir nicht mehr folgendes machen:

*zeiger = Wert; // Fehler!

    Da der Zeiger ja nirgends zeigt. Bei der Speicher-Reservierung/Zuweisung die wir nächstes mal machen werden werdet ihr sehen das wir auch nicht mehr Speicher Zuweisen können, etwas das wir jedes mal vor der Speicherzuweisung checken müssen (zeiger == NULL)!

Zeigerarithmetik

    Da Zeiger ja Speicheradressen (Ganzzahlen) abspeichern können wir mit denen viele mathematische Operationen ausführen...

   Wie vorhin bereits erklärt addieren wir ja den Abstand vom ersten Index zu den gewünschten Index um an gewisse Werte von Feldern zuzugreifen (etwas das im Hintergrund passiert). Das heißt das wir die Ganzzahl Speicheradresse die in einem Zeiger abgespeichert ist addieren und subtrahieren können wie folgend:

zeiger = zeiger + Ganzzahl;
zeiger += Ganzzahl; // das selbe wie zeiger = zeiger + Gahzzahl
zeiger = zeiger++; // '1' erhören
zeiger = zeiger--; // '1' erniedrigen

usw.

    Da Zeiger Ganzzahlen abspeichern können wir diese Werte mit den Vergleichsoperationen vergleichen.

Zeichenketten

    Eine Zeichenkette ist nicht mehr als eine Zeichenfeld (char array). Wie wir bereits letztes mal erwähnt haben hat jede Zeichenkette ein '\0' (oder NULL) beim letzten Index so das Funktionen wie printf() und die meisten Bibliotheksfunktionen von string.h wissen wo die Zeichenkette aufhört.

Das heißt das eine Zeichenkette wie folgend definiert wird:

char Name[N];

    Wo N die maximale Anzahl an Zeichen ist. Natürlich können wir dadurch nur (N-1)-lange Zeichenketten abspeichern, da das letze Zeichen ja '\0' ist!

    Eine Zeichenkette muss nicht unbedingt statisch sein aber kann auch dynamisch sein indem man einen Zeiger benutzt wie den folgenden:

char *Name;

    Um aber was jetzt hier abzuspeichern müssen wir Speicher zuweisen, das wir nächstes mal machen werden! Ähnlich wie bei den Feldern können wir eine Zeichenkette auch direkt initialisieren und können dadurch auch die Größe auslassen. Das sieht aus wie folgend:

char Name[] = "Drifter";

Diese Linie kreiert dadurch ein Feld das 7+1 = 8-Werte abspeichert.

Wir könnten jetzt z. B. ein gewisses Zeichen ausdrucken wie folgend:

printf("%c", Name[4]);

    Die vorherige Funktion würde den fünften Buchstaben ausdrucken (vergisst nie das wir von 0 anfangen!) der natürlich 't' ist!

Mit dem folgenden Code können wir eines der Zeichen ändern:

Name[3] = 't'; // 'f' -> 't'

Die ganze Zeichenkette kann mit printf() wie folgend ausgedruckt werden:

printf("%s\n", Name); // das "Dritter" ausdruckt

Bibliotheksfunktionen:

    Da Zeichenketten (Strings) sehr nützlich sind gibt es eine Standardbibliothek die Funktionen zur Bearbeitung und Verwendung von Zeichenketten enthält. Die Bibliothek heißt: "string.h" und ist natürlich eine Headerdatei die mit "include" eingebunden werden muss.

Diese Funktionen sind:

  • strcpy -> Zum kopieren (copy) einer Zeichenkette von einer Quelle zu einem Ziel (damit können wir z. B. eine Zeichenkette initialisieren).
  • strncpy -> Ähnlich wie "strcpy" nur das wir jetzt auch die Anzahl (number) an Zeichen definieren.
  • strcat -> Um zwei Zeichenketten miteinander zu verbinden (vom Englischen: concatenation).
  • strncat -> Das gleiche wie "strcat" aber nur für einen Abschnitt/Teil der zweiten Zeichenkette.
  • strtok -> Um ein gegebenes Trennzeichen (token) anzuzeigen (nützlich um spezifische Zeichen bei einem Text zu finden)
  • strspn -> Diese Funktion gibt uns die Anzahl (span) an Zeichen von einer Zeichenkette die bei einer zweiten Zeichenkette enthalten sind.
  • strcspn -> Diese Funktion gibt uns die Anzahl (span) an Zeichen von einer Zeichenkette die nicht bei einer zweiten Zeichenkette vorkommen.
  • strpbrk -> Diese Funktion gibt einen Zeiger auf das erste Zeichen von der ersten Zeichenkette zurück das auch bei der zweiten Zeichenkette vorkommt (Bei einer erfolglosen Suche gibt diese NULL zurück).
  • strchr -> Diese Funktion sucht nach dem ersten Auftreten eines Zeichens bei einer Zeichenkette und gibt einen Zeiger auf diesen Auftritt zurück. (NULL wenn erfolglos)
  • strrchr -> Diese Funktion ist ähnlich wie "strchr". Der Unterschied ist das wir jetzt nach dem letzten Auftritt suchen.
  • strcmp -> Um Zeichenketten zu vergleichen. Ein Wert von 0 sagt uns das die Zeichenketten identisch sind. Jeder andere Wert Zeigt uns den ASCII-Unterschied zwischen den Zeichenketten. Positiv heißt das die erste größer ist. Negative das die zweite größer ist.
  • strncmp -> Ähnlich wie "strcmp" nur das wir jetzt N-Zeichen vergleichen.
  • strlen -> Gibt uns die Länge einer Zeichenkette (ohne das Nullzeichen) zurück.
  • strstr -> Diese Funktion sucht nach dem ersten Vorkommen einer Zeichen-kette/folge bei einer Zeichenkette und gibt einen Zeiger auf diesen Auftritt zurück. (NULL wenn erfolglos)

Coole Funktionen! 

Dateiverarbeitung

    Dateien sind Daten die wir im physikalischen Speicher und nicht in der RAM abspeichern. Durch diese Dateien können wir Informationen/Berechnungen aus der Programmausführung abspeichern so das wir zu denen zugreifen können nach dem das Programm beendet ist und so das wir diese auch bei einer erneuten Ausführung in Anspruch nehmen können. Wir können z. B. Statistiken aus einem Feld (oder aus Feldern) in einer Datei abspeichern so das wir dieses Feld dann erneut einfügen/neuladen können. Dateien könnten auch eine Eingabe zum Programm sein. Unser Beispielprogramm das gleich folgt wird z. B. einen Text bearbeiten und Wörter, Buchstaben und andere Informationen ausdrucken und sogar in eine neue Datei schreiben!

Definition

   Also wie sehen Dateien in C aus? Durch die Einbindung der "stdio.h" wird ein neuer Datentyp hinzugefügt, der sogenannte "FILE" Datentyp. Wir deklarieren aber keine Variabel sondern einen Zeiger an diese FILE's. Der Code für einen solchen Zeiger sieht wie folgend aus:

FILE * fp;

Wir nennen diesen Zeiger meistens "fp" das vom Englischen: "File Pointer" kommt.

Schritte zur Verarbeitung

Bei Dateien können wir jegliche Operationen ausführen.

Die Schritte die ihr jedes mal ausführen solltet sind:

  1. Datei öffnen oder erstellen
  2. Informationen lesen oder schreiben
  3. Datei wieder schließen

    Das letze ist sogar das wichtigste! Bei manchen Betriebssystemen kann die Datei auch offen bleiben bis zum Neustart des ganzen Systems!

Öffnen, Erstellen und Schließen

Zum öffnen und erstellen wird die Funktion fopen() verwendet die wie folgend aussieht:

fp = fopen(datei, modus);

    Wie ihr sehen könnt gibt diese Funktion einen Zeigen zu einer "Datei" zurück. Der Parameter "datei" ist nicht unbedingt ein Name sondern kann auch eine Adresse sein. Der Parameter "modus" kann die folgenden Werte haben:

  • "r" -> Zum lesen (read)
  • "w" -> Zum schreiben (write)
  • "a" -> Zum anhängen (append)
  • "b" -> Binärmodus (binary)
  • "t" -> Textmodus (text)
  • und mehr..

Um eine Datei mit den Namen: "Beispiel" zum lesen zu öffnen (oder erstellen) benutzen wir:

fp = fopen("Beispiel.txt", "r");

    Wenn keine Datei mit dem Namen "Beispiel.txt" bei dem Ordner wo sich das Programm befindet ist, dann wird eine neue Datei mit diesen Namen automatisch erstellt. Der Name ist natürlich ein Dateipfad und wenn ihr wisst wie man diese bei Windows oder Linux benutzt könnt ihr die Datei auch von irgendwo in eurer Festplatte nehmen. 

Zum schließen einer Datei benutzen wir:

fclose(fp);

Wenn mehrere Dateien offen sind kann man auch fcloseall() benutzen!

Ihr solltet ein paar Dinge beachten:

  • Wenn man eine Datei die bereits existiert zum "Schreiben" öffnet dann wird der komplette Inhalt ohne Warnung gelöscht.
  • Beim "Αnhänge-modus" hat man das vorherige Problem nicht da die neuen Informationen am "Ende" geschrieben werden.

Lesen und schreiben

Zum lesen und schreiben können wir die folgenden Funktionen verwenden:

  • fprintf(fp, "", ...) -> funktioniert ähnlich wie printf()
  • fscanf(fp, "", ...) -> funktioniert ähnlich wie scanf()
  • fgets(buf, N, fp) -> ähnlich wie gets() (werdet ihr beim Beispiel besser verstehen)
  • und mehr...

    Beim lesen und schreiben wird der Zeiger im Hintergrund erhöht. Beim lesen gibt es natürlich ein Datenende und dazu gibt es in C einen Spezialwert "EOF" den wir auch beim lesen immer verwenden werden (while !EOF).

Beispielprogramm

Sagen wir mal wir haben den folgenden Text:

 "Wedlock suits you," he remarked. "I think, Watson, that you have

put on seven and a half pounds since I saw you."


"Seven!" I answered. 

Dieser ist bei einer Datei mit dem Namen: "text.txt" eingespeichert:

Wir wollen folgendes tun:

  • Finden wie viel male jedes Wort vorkommt (ein Wort hat mindestens 2 Zeichen und keine Nummern)
  • Finden wie viele Wörter mit einen spezifischen Buchstaben (Eingabe vom Benutzer) anfangen und diese ausdrucken.
  • Finden wie viele Wörter mit einen spezifischen Buchstaben enden und diese ausdrucken.
  • Berechnen wie viele mal jeder Buchstabe des Alphabets vorkommt.
  • Alle Wörter in einer neuen Datei mit dem Namen: "words.txt" in alphabetischer Reihenfolge und mit ersten und letzten Buchstaben kapitalisiert (großgeschrieben) ausdrucken.

    Damit der Code einfacher zu lesen ist werden wir Funktionen benutzen die ganz einfach sein werden und nicht mal Parameter haben (werde ja einen kompletten Artikel für diese machen). Weil die Funktionen keine Parameter haben werde ich auch die meisten Variablen Global deklarieren!

    Nach der Ausführung sollte eine neue Datei mit den Namen: "words.txt" bei dem Verzeichnis des Programms sein!

Der Code ist wie folgend:

Kommentare haben nun keine Umlaute mehr so das es keine Probleme gibt! :)

Die Ausführung sieht wie folgend aus:

Und wir haben natürlich die folgende Datei als Ausgabe:

Ihr könnt den Code bei dem folgenden Links von pastebin runterladen:

Kommentare ändern sich nur...

Referenzen

  1. https://de.wikipedia.org/wiki/Zeiger_in_C
  2. http://www.c-howto.de/tutorial/zeiger/
  3. http://www.c-howto.de/tutorial/strings-zeichenketten/
  4. https://de.wikibooks.org/wiki/C-Programmierung:_Zeichenkettenfunktionen
  5. http://www.c-howto.de/tutorial/dateiverarbeitung/

Vorherige Artikel

Einführung -> Programmiersprachen, die Sprache C, Anfänger Programme 

Felder ->  Felder, Felder in C, Erweiterung des Anfänger Programms

  Und das war's dann auch mit diesem Artikel und ich hoffe ich hab alles verständlich erklärt. Νächstes mal werden wir über die dynamische Speicherzuordnung reden das wir bei dynamischen Feldern(Arrays) benuzten.

Tschüss!

Sort:  

Congratulations @drifter2! You have completed the following achievement on Steemit and have been rewarded with new badge(s) :

Award for the number of upvotes received

Click on the badge to view your Board of Honor.
If you no longer want to receive notifications, reply to this comment with the word STOP

Do not miss the last post from @steemitboard:
SteemitBoard World Cup Contest - Semi Finals - Day 1


Participate in the SteemitBoard World Cup Contest!
Collect World Cup badges and win free SBD
Support the Gold Sponsors of the contest: @good-karma and @lukestokes


Do you like SteemitBoard's project? Then Vote for its witness and get one more award!

Coin Marketplace

STEEM 0.29
TRX 0.12
JST 0.033
BTC 63464.16
ETH 3111.33
USDT 1.00
SBD 3.98