• Keine Ergebnisse gefunden

¨Ubungspaket 30 Kopieren von Dateien

N/A
N/A
Protected

Academic year: 2021

Aktie "¨Ubungspaket 30 Kopieren von Dateien"

Copied!
13
0
0

Wird geladen.... (Jetzt Volltext ansehen)

Volltext

(1)

Ubungspaket 30 ¨

Kopieren von Dateien

Ubungsziele: ¨

1. ¨ Offnen und Schließen von Dateien 2. Einfaches Lesen und Schreiben 3. Behandlung der

” EOF-Marke“

4. Kopieren ganzer Dateien Skript:

Kapitel: 59 bis 67 und insbesondere ¨ Ubungspaket 25 Semester:

Wintersemester 2021/22 Betreuer:

Thomas, Tim und Ralf Synopsis:

Wie schon im Skript geschrieben, ist das Arbeiten mit Dateien m¨ uhsam und immer wieder voller ¨ Uberraschungen, selbst f¨ ur fortgeschrittene Programmierer. In so einer Situation hilft nur eines: intensiv ¨ Uben.

In diesem ¨ Ubungspaket schauen wir uns die wichtigsten Funktionen aus dem Paket stdio.h an, ¨ uben den richtigen Umgang mit den Ein- und Ausgabeformatierungen und entwickeln ein kleines Programm, das

¨ ahnlich dem Unix/Linux Programm cat Dateien kopiert.

(2)

Teil I: Stoffwiederholung

Vorab nochmals eine wesentliche Bemerkung aus dem Skript: Die Dateischnittstelle ist eine sehr schwere Sache! Wenn man denkt, man hat’s verstanden, kommt meist’ doch wieder etwas neues hinzu. W¨ahrend des Lernens kommt man sich oft vor,

”wie ein Schwein, dass in’s Uhrwerk schaut.“ Nichtsdestotrotz versuchen wir uns mittels dieses ¨Ubungspaketes der Dateischnittstelle ein wenig auf freundschaftlicher Basis zu n¨ahern. Also: Good Luck!

Aufgabe 1: Die Dateischnittstelle FILE/*FILE

Zuerst einmal besch¨aftigen wir uns mit den relevanten Dateien und Datenstrukturen.

1. Welche Datei muss bzw. sollte man einbinden, wenn man etwas einliest oder ausgibt?

2. Nun zur eigentlichen FILE-Datenstruktur: Diese haben wir außerordentlich verein- facht in Skriptkapitel 63 erl¨autert. Ziel war es, einen ersten Eindruck hiervon zu vermitteln. F¨ur das weitere Vorgehen ist es sinnvoll, sich die Datei stdio.h einmal genauer anzuschauen. Sie befindet sich unter Linux im Verzeichnis /usr/include bzw. unter Windows im include-Verzeichnis des installierten Compiler wie beispiels- weise C:\MinGW\include. Diese Datei ist sehr lang. Trotzdem einfach mal hinein- schauen, die Betreuer werden gerne weiterhelfen. Sucht diese Datei und benennt die wesentlichen Komponenten der FILE-Datenstruktur. Hinweis: Linux-Nutzer schauen sich auch die Datei libio.han, die sich ebenfalls unter /usr/include befindet.

3. Schauen wir uns noch kurz die folgenden Fragen zur FILE-Datenstruktur an:

Arbeiten wir direkt mit der FILE-Datenstruktur?

Wer arbeitet mit der FILE-Datenstruktur?

(3)

Aufgabe 2: Detailfragen zu den Dateizugriffen

An dieser Stelle sei angemerkt, dass der DatentypFILE *(etwas irref¨uhrend) vielfach auch File Pointer genannt wird.

Wie heißt der File Pointer f¨ur die Standardeingabe?

Wie heißt der File Pointer f¨ur die Fehlerausgabe?

Wie heißt der File Pointer f¨ur die Standardausgabe?

Mit welcher Funktion ¨offnet man Dateien?

Welchen R¨uckgabewert hat diese Funktion?

Wie erkennt man einen Fehler beim ¨Offnen?

Welcher mode ¨offnet zum Lesen?

Welcher mode ¨offnet zum Schreiben?

Beispiel f¨ur das ¨Offnen zum Lesen:

Beispiel f¨ur das ¨Offnen zum Schreiben:

Wie unterscheiden sich die Funktionen scanf()und fscanf()beziehungsweise printf() und fprintf() voneinander?

(4)

Teil II: Quiz

Aufgabe 1: Eingabe gemischter Datentypen

Auch wenn man schon eine ganze Weile dabei ist, so sind viele doch immer wieder davon

¨uberrascht, wie in der Programmiersprache C die Eingabe verarbeitet wird. Zu diesem Thema haben wir folgendes Quiz entwickelt. Zun¨achst aber mal einige Anmerkungen:

• Wir haben ein C-Programm, das die Funktion scanf()mit verschiedenen Formaten aufruft. Jeder Aufruf von scanf() liest immer nur einen Parameter ein. Anschlie- ßend wird sofort der R¨uckgabewert der Funktionscanf()sowie der eingelesene Wert ausgegeben.

• Die Routinen f¨ur die eigentliche Ein-/Ausgabe haben wir ausgelagert und am Ende dieses ¨Ubungsteils abgedruckt. Alle Funktionen haben einen Namen, der immer mit

’do ’ anf¨angt und mit dem entsprechenden Datentyp aufh¨ort. Beispiel: do int() verarbeitet einen Wert vom Typ int.

• Das Programm liest und schreibt immer auf die Standardein- bzw. -ausgabe.

• Im Hauptprogramm werden die entsprechenden Routinen aufgerufen und mit der ent- sprechenden Formatangabe versorgt. Mit anderen Worten: Aus dem Funktionsnamen und der Formatangabe l¨asst sich die Ausgabe direkt ableiten.

• Eure Aufgabe ist es nun, sich die Programmeingabe anzuschauen und vorherzusagen, was die entsprechenden Ausgaben sein werden. Dazu ist es durchaus ratsam, sich nochmals eben die Dokumentation der Formatierungen %d, %c und %e anzuschauen.

• Die Eingabedatei bzw. die Eingabedaten sehen wie folgt aus:

Die Eingabedatei:

1 12 2

3 56

4 1 2 3 4 5 987 AB 123 5 abc

Die Daten zeichenweise dargestellt:

1 2 \n \n 5

6 \n 1 2 3 4 5

9 8 7 A B 1 2

3 \n a b c EOF

• Zur besseren Veranschaulichung haben wir die Funktion debug char() geschrieben, die die Ausgaben etwas aufbereitet: Sonderzeichen wir Tabulator und Zeilenwechsel werden zus¨atzlich als C-Escape-Sequenzen dargestellt.

• Zur Erinnerung: Die Standardein- und -ausgabe kann mittels < und > umgeleitet werden, ohne dass das Programm davon etwas bemerkt. Beispiel:a.exe < test.txt

(5)

Das Programm:

1 int m a i n ( int argc , c h a r ** a r g v )

2 {

3 d o _ i n t ( " % d " ) ; 4 d o _ i n t ( " % d " ) ; 5 d o _ c h a r ( " % c " ) ; 6 d o _ c h a r ( " % c " ) ; 7 d o _ i n t ( " %3 d " ) ; 8 d o _ c h a r ( " % c " ) ; 9 d o _ i n t ( " % d " ) ; 10 d o _ c h a r ( " % c " ) ; 11 d o _ i n t ( " % d " ) ; 12 d o _ c h a r ( " % c " ) ; 13 d o _ i n t ( " % d " ) ;

14 d o _ n c h a r ( " %4 c " , 4 ) ; 15 d o _ i n t ( " % d " ) ;

16 }

Die Funktionendo int(),do char()unddo nchar()befinden sich auf der n¨achsten Seite.

Die Eingabedatei:

zeichenweise dargestellt

1 2 \n \n 5 6 \n 1 2 3 4 5

9 8 7 A B 1 2 3 \n a b c EOF

Die auszuf¨ullende Tabelle: Was wird wie abgearbeitet?

Zeile Datentyp res Wert Anmerkungen

3 int 1 12 ein int eingelesen, wie zu erwarten war

4 . . . . . . . . . . . . . . . . .

5 . . . . . . . . . . . . . . . . .

6 . . . . . . . . . . . . . . . . .

7 . . . . . . . . . . . . . . . . .

8 . . . . . . . . . . . . . . . . .

9 . . . . . . . . . . . . . . . . .

10 . . . . . . . . . . . . . . . . .

11 . . . . . . . . . . . . . . . . .

12 . . . . . . . . . . . . . . . . .

13 . . . . . . . . . . . . . . . . .

14 . . . . . . . . . . . . . . . . .

15 . . . . . . . . . . . . . . . . .

(6)

Die einzelnen, sehr komprimierten Verarbeitungsroutinen:

1 # i n c l u d e < s t d i o . h >

2

3 v o i d d o _ i n t ( c h a r * fmt )

4 {

5 int i , res = s c a n f ( fmt , & i ) ; 6 p r i n t f ( " int : res =%2 d " , res ) ;

7 if ( res == 1 )

8 p r i n t f ( " i =% d \ n " , i ) ; 9 e l s e p r i n t f ( " i = - - -\ n " ) ;

10 }

11

12 v o i d d e b u g _ c h a r ( c h a r c , int ok ) // p r e t t y p r i n t c h a r

13 {

14 if ( ! ok )

15 p r i n t f ( " - - - " ) ;

16 e l s e if ( c == ’ \ t ’ )

17 p r i n t f ( " \\ t " ) ;

18 e l s e if ( c == ’ \ n ’ )

19 p r i n t f ( " \\ n " ) ; 20 e l s e p r i n t f ( " % c " , c ) ;

21 }

22

23 v o i d d o _ c h a r ( c h a r * fmt )

24 {

25 c h a r c ;

26 int res = s c a n f ( fmt , & c ) ;

27 p r i n t f ( " c h a r : res =%2 d c = " , res ) ; 28 d e b u g _ c h a r ( c , res == 1 ) ;

29 p r i n t f ( " \ n " ) ;

30 }

31

32 v o i d d o _ n c h a r ( c h a r * fmt , int len )

33 {

34 int i , res ;

35 c h a r c [ 10 ];

36 res = s c a n f ( fmt , c ) ; // or & c [ 0 ] 37 p r i n t f ( " c h a r : res =%2 d c = " , res ) ;

38 for ( i = 0; i < len ; i ++ )

39 d e b u g _ c h a r ( c [ i ] , res > 0 ) ; 40 p r i n t f ( " \ n " ) ;

41 }

(7)

Teil III: Fehlersuche

Aufgabe 1: Z¨ ahlen von Vokalen in einer Datei

Diesmal hat sich unser Programmierer Dr. V. Error versucht. Sein Programm soll die Anzahl der Vokale in einer Datei ermitteln. Bei der Dateiverarbeitung hat er aber nicht ganz aufgepasst. Finde und korrigiere die Fehler in folgendem Programm:

1 # i n c l u d e < s t d i o . h >

2

3 int c n t V o w e l s ( F I L E fp )

4 {

5 int c , cnt ;

6 for ( cnt = 0; ( c = g e t c () ) != EOF ; )

7 cnt += c == ’ a ’ || c == ’ e ’ || c == ’ i ’ || c == ’ o ’

8 || c == ’ A ’ || c == ’ E ’ || c == ’ I ’ || c == ’ O ’

9 || c == ’ u ’ || c == ’ U ’ ;

10 r e t u r n cnt ;

11 }

12

13 int m a i n ( int argc , c h a r ** a r g v )

14 {

15 int i , cnt ;

16 int fp ;

17 for ( i = 1; i < a r g c ; i ++ )

18 if (( fp = f o p e n ( a r g v [ i ] , " w " ) ) != 0 )

19 {

20 cnt = c n t V o w e l s ( fp ) ;

21 p r i n t f ( stdout , " ’% s ’: % d v o k a l e \ n " , a r g v [ i ] , cnt ) ;

22 f c l o s e ( * fp ) ;

23 }

24 e l s e p r i n t f ( stderr ,

25 " ’% s ’: f e h l e r b e i m o e f f n e n \ n " , a r g v [ i ] ) ;

26 }

(8)

Teil IV: Anwendungen

Das Ziel des Anwendungsteils ist es, die Funktion des Unix/Linux-Kommandoscatteilwei- se nachzuimplementieren. Dieses Kommando macht nichts anderes als eine oder mehrere Dateien in eine andere zu kopieren. F¨ur den Programmieranf¨anger ist diese Aufgabe schon nicht mehr ganz einfach, da sie einerseits einen (relativ einfachen) inhaltlichen Teil (das Kopieren einer Datei in eine andere) hat, andererseits auch eine Einbindung in die

”reale“

Computerwelt beinhaltet. Um hier klar zu sehen, gehen wir in drei Schritten vor: Zuerst diskutieren wir die generelle Aufgabenstellung, dann k¨ummern wir uns um die eigentliche Funktionalit¨at und letztlich besch¨aftigen wir uns mit der Parameterversorgung.

Vor¨ uberlegungen: Design des cat-Kommandos

Nochmals von vorne: Das Linux cat-Kommando kopiert eine oder mehrere Dateien in eine neue Datei. Dabei macht es sich einige Eigenschaften des Betriebssystems, insbesondere das Umlenken von Dateien zunutze. Das bedeutet: dascat-Kommando schreibt seine Aus- gaben immer auf die Standardausgabe, also in der Regel auf den Bildschirm. Will man die Ausgaben, was meist der Fall ist, in einer anderen Datei haben, muss man die Bildschirm- ausgaben einfach umlenken. Ok, hier ein paar Beispiele:

cat Kopiert die Tastatureingaben auf den Bildschirm.

cat datei-1 Kopiert die Datei datei-1 auf den Bildschirm.

cat d-1 d-2 ... d-n Kopiert die Datei d-1, d-2 . . .d-n nacheinander auf den Bildschirm.

cat > out Kopiert die Tastatureingaben in die Datei out cat in-1 > out Kopiert die Datei in-1 in die Datei out

cat in-1 ... in-n > out Kopiert die Dateiin-1. . .in-nnacheinander in die Datei out

cat - Kopiert die Tastatureingabe (die Datei -) auf den Bild- schirm.

cat d-1 - d-2 Kopiert zuerst die Datei d-1, dann die Tastatureingabe und schließlich die Datei d-2 auf den Bildschirm.

Soweit, so gut. Wichtig f¨ur das Verst¨andnis ist nun, dass das Kommando gar nicht merkt, dass es die Ausgaben nicht auf den Bildschirm sondern ggf. in eine Datei schreibt. Dieses Umlenken erledigt das Betriebssystem und ist (nahezu) f¨ur das Kommando unsichtbar. Das heißt, das Kommando catschreibt prinzipiell immer und alles auf die Standardausgabe.

Was k¨onnen wir aus obiger Beschreibung an Informationen f¨ur uns herausziehen?

(9)

2. catliest alle Dateien nacheinander. Das heißt, dass man nur eine Funktion ben¨otigt, die von einem File Pointer liest und auf den anderen schreibt. Sollten mehrere Da- teien gelesen werden, muss man diese Funktion nur immer wieder mit einem anderen Eingabeparameter aufrufen.

3. Sollte catkeinen Parameter bekommen, liest es von der Standardeingabe.

Was heißt das f¨ur unser Programm? Wir brauchen eine Funktion copyFile(), die von einem File Pointer liest und auf einen anderen schreibt. Um m¨oglichst flexibel zu bleiben, sehen wir zwei Parameter vor, auch wenn der zweite immer mitstdoutbelegt werden wird.

Um diese (recht einfache) Funktion zu testen, rufen wir sie einfach mit der Standardein- und Standardausgabe auf.

Ferner brauchen wir dann noch ein Hauptprogramm, das je nach Parameterlage die einzel- nen Dateien ¨offnet, die FunktioncopyFile() entsprechend aufruft und die Dateien wieder schließt. Und genau das machen wir jetzt in den n¨achsten beiden Aufgaben.

Aufgabe 1: Kopieren einer Datei

1. Aufgabenstellung

Entwickle eine FunktioncopyFile, die Dateien zeichenweise liest und wieder ausgibt.

Nach den oben bereits angestellten Vor¨uberlegungen sollten die folgenden Fragen einfach zu beantworten sein:

Welche Parameter ben¨otigt die Funktion?

Welchen Typ haben die beiden Parameter?

Welchen Typ sollte die Funktion haben?

Was f¨ur ein R¨uckgabewert ist sinnvoll?

Hinweis:F¨ur das Weiterarbeiten empfiehlt es sich, die Dokumentation der Funktion fgetc() und scanf() hinsichtlich des Lesens von Zeichen und des Erreichens des Dateiendes anzuschauen.

2. Pflichtenheft: Aufgabe, Parameter, R¨uckgabewert, Schleifenbedingung

(10)

Als Testdateien bieten sich sowohl die Standardeingabe als auch die eigens erstellten C-Dateien an.

Beispiele: Kommando Bedeutung

mycat Lesen der Standardeingabe

mycat < mycat.c Lesen der Dateimycat.c¨uber die Standardeingabe mycat > out.txt Lesen der Standardeingabe, Schreiben in die Datei

out.txt Hinweise f¨ur das interaktive Testen:

Linux-Nutzer:

Linux-Nutzer k¨onnen das Ende der Standardeingabe einfach durch Dr¨ucken von STRG-D(bzw.CTRL-D) (gleichzeitiges Dr¨ucken der TastenSTRGundD) erzeugen.

Windows-Nutzer:

Unter Windows hat das interaktive Testen folgenden Haken: Der Beispielquell- text funktioniert nicht, wenn man das Zeichen EOF nicht am Zeilenanfang ein- gibt. Auf der Windows-Konsole cmd wird EOF durch die Tastenkombination STRG-Z erzeugt. Auch ist es immer notwendig, nach der Eingabe von STRG-Z auch noch die Enter-Taste zu bet¨atigen. Dieses Verhalten liegt aber nicht an unserem C-Programm sondern wird durch eine andere Instanz gesteuert (ver- mutlich der Treiber f¨ur die Tastatureingabe der Kommandozeile), die außerhalb des Fokus‘ dieser Lehrveranstaltung liegt.

Absolut garstig wird die ganze Sache, wenn man beispielsweise zuerst die Stan- darteingabe in eine Datei kopiert und dabeiSTRG-Z schon mal irgendwo mitten in einer Zeile hatte. Wie gerade gesagt, wird das mitten in einer Zeile stehende STRG-Z nicht als Ende der Eingabe erkannt und daher mit in die Datei ge- schrieben. Auch die nach demSTRG-Z kommenden Zeichen werden in die Datei kopiert. Das sieht man, wenn man sich die Datei mit einem Editor anschaut.

Das Programm endet ja erst, wenn STRG-Z am Anfang einer Zeile eingegeben wird! Und jetzt lasst das Programm mal genau diese Datei ausgeben . . .

(11)

4. Implementierung

5. Kodierung

(12)

Aufgabe 2: Das komplette cat-Kommando

1. Aufgabenstellung

Erg¨anze die in der vorherigen Aufgabe entwickelte Funktion copyFile() um ein entsprechendes Hauptprogramm, das die angeforderten Dateien ¨offnet und schließt.

Entsprechende Beispiele haben wir zu Gen¨uge in den Vor¨uberlegungen besprochen.

Es sei nochmals daran erinnert, dass der Dateiname - f¨ur die Tastatur steht.

2. Pflichtenheft: Aufgabe, Ausgabe, Parameter, Sonderf¨alle

3. Implementierung

(13)

4. Kodierung

5. Testdaten

Das Komplettprogramm kann jetzt einfach so getestet werden, wie wir es in den Vor¨uberlegungen diskutiert haben.

Referenzen

ÄHNLICHE DOKUMENTE

• Eine Zeile “− k” (− und k sind durch ein Leerzeichen getrennt) gibt an, dass Schl¨ ussel k gel¨ oscht

In den Dateien Ue9 small.txt, Ue9 medium.txt und Ue9 large.txt sind gerichtete Gra- phen wie folgt gespeichert:. • Die Knoten des Graphen sind

In den Dateien Ue10 small.txt, Ue10 medium.txt und Ue10 large.txt sind ungerich- tete Graphen wie folgt gespeichert:. • Die Knoten des Graphen sind

In den Dateien Ue11 small.txt, Ue11 medium.txt und Ue11 large.txt sind gewichtete ungerichtete Graphen wie folgt gespeichert:. • Die Knoten des Graphen sind

In den Dateien Ue12a.txt, Ue12b.txt und Ue12c.txt sind gewichtete gerichtete Graphen wie folgt gespeichert:. • Die Knoten des Graphen sind

In der Datei Ue13.txt ist ein Flussnetzwerk wie folgt gespeichert:. • Die Knoten des Flussnetzwerkes sind

f¨ur dessen Fl¨acheninhalt A und zeige ferner, dass die H¨ohe auf jene der drei Seiten dieses Dreiecks, welche dem Mittelpunkt des Hypotenusenquadrats gegen¨uberliegt, durch

4 Wenn 2-seitig → 2-seitig in Schritt 2 gewählt wurde, wählen Sie Lange Kante oder Kurze Kante als Bindeposition für die Duplex (beidseitige) Kopie, und drücken Sie dann