• Keine Ergebnisse gefunden

¨Ubungspaket 30 Kopieren von Dateien

N/A
N/A
Protected

Academic year: 2021

Aktie "¨Ubungspaket 30 Kopieren von Dateien"

Copied!
14
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?

#include <stdio.h>, denn hier befinden sich alle wesentlichen Definitionen

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.

Auf vielen Systemen sieht die FILE-Datenstruktur wie folgt aus:

t y p e d e f s t r u c t _ i o b u f {

c h a r * _ p t r ; int _ c n t ; c h a r * _ b a s e ; int _ f l a g ; int _ f i l e ; int _ c h a r b u f ; int _ b u f s i z ; c h a r * _ t m p f n a m e ;

} F I L E ;

Das sieht f¨ur Anf¨anger und leicht Fortgeschrittene recht kryptisch aus. Aber jeder kann ja mal raten, wof¨ur die einzelnen Eintr¨age zust¨andig sind.

Das Sch¨one an der Sache ist, dass wir gar nicht direkt mit den einzelnen Kompo- nenten arbeiten; dies ¨uberlassen wir den einzelnen abstrakten Funktionen.

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

Arbeiten wir direkt mit der FILE-Datenstruktur? Nein :-)

(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? stdin Wie heißt der File Pointer f¨ur die Fehlerausgabe? stderr Wie heißt der File Pointer f¨ur die Standardausgabe? stdout

Mit welcher Funktion ¨offnet man Dateien? FILE *fopen(char *fname,char *mode) Welchen R¨uckgabewert hat diese Funktion? Ein neuer File Pointer vom Typ FILE * Wie erkennt man einen Fehler beim ¨Offnen? Der R¨uckgabewert ist ein Null-Zeiger Welcher mode ¨offnet zum Lesen? "r"

Welcher mode ¨offnet zum Schreiben? "w"

Beispiel f¨ur das ¨Offnen zum Lesen: FILE *fp = fopen("bsp-rd.txt", "r") Beispiel f¨ur das ¨Offnen zum Schreiben: FILE *fp = fopen("bsp-wt.txt", "w") Wie unterscheiden sich die Funktionen scanf()und fscanf()beziehungsweise printf() und fprintf() voneinander?

Die Funktionenscanf()und printf()sollten bereits hinl¨anglich bekannt sein. scanf() liest Eingaben von der Tastatur und printf()gibt auf den Bildschirm aus. Oder? Nun, prinzipiell jein. Korrekt ist die Aussage: scanf() liest von der Standardeingabe und printf() gibt auf die Standardausgabe aus. Im Regelfall entsprechen aber die Stan- dardeingabe der Tastatur und die Standardausgabe dem Bildschirm. Die Funktionen fscanf() und fprintf() ¨ahneln den obigen Funktionen mit dem Unterschied, dass bei diesen beiden Funktionen die Quelle, bzw. das Ziel, explizit angegeben werden m¨ussen.

Als Angabe f¨ur Quelle und Ziel verwendet man einen File Pointer (eine Variable vom Typ FILE *). Insofern sind printf( "Hallo Welt\n" ) undfprintf(stdout, "Hallo Welt\n" ) identisch, ebenso scanf( "%d", & i ) und fscanf( stdin, "%d", & i ).

(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 resultierenden Ausgaben:

1 int : res = 1 i =12

2 int : res = 1 i =56

3 c h a r : res = 1 c =\ n 4 c h a r : res = 1 c =1 5 int : res = 1 i = 2 3 4 6 c h a r : res = 1 c =5 7 int : res = 1 i = 9 8 7 8 c h a r : res = 1 c = A 9 int : res = 0 i = - - - 10 c h a r : res = 1 c = B 11 int : res = 1 i = 1 2 3 12 c h a r : res = 1 c =\ n a b c 13 int : res = -1 i = - - -

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 int 1 56 Leerzeichen, 2 Zeilenwechsel, 2 Leerzeichen ¨uberlesen,int eingelesen, alles normal

5 char 1 ’\n’ Der Zeilenwechsel ist also das n¨achste Zeichen 6 char 1 ’1’ Und jetzt schon die Ziffer ’1’

7 int 1 234 Endlich wieder ein int aber nur drei Ziffern :-(

8 char 1 ’5’ Hier jetzt die Ziffer ’5’als Zeichen 9 int 1 987 Wieder ganz normal ein intgelesen 10 char 1 A Ok, das Zeichen direkt hinter der Zahl

11 int 0 --- Nanu, keine Zahl gefunden? ’B’ist ja auch keine Zahl 12 char 1 ’B’ Aha, wir stehen noch beim ’B’, ist auch keine Zahl 13 int 1 123 Puhhh, wieder alles normal

14 char 1 \nabc Interessant, mit%4cwerden direkt vier Zeichen eingelesen und in einem Array abgelegt

15 int -1 --- Das Dateiende bemerken wir also mit res == EOF

(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 }

Zeile Fehler Erl¨auterung Korrektur

3 * fehlt Hier m¨usste ein Zeiger stehen, da sonst der Aufru- fer m¨ogliche Ver¨anderungen der FILE-Datenstruktur nicht mitbekommt.

FILE *fp

. . . .

6 fp fehlt Die Funktiongetc()erfordert einen File Pointer, da- mit sie weiß, von wo sie lesen soll.

getc( fp )

(8)

Zeile Fehler Erl¨auterung Korrektur 16 int statt

FILE *

Funktionen wie getc() und fprintf() ben¨otigen Zeiger vom Typ FILE. Es gibt auch Funktionen, die hier int-Parameter erwarten; diese besprechen bzw.

verwenden wir aber nicht.

FILE *

. . . .

18 "w" statt "r" Wenn man von DateienLesen will, muss man sie mit- tels "r"¨offnen.

"r"

. . . .

21 stdout zu viel

Die Funktionprintf()schreibtimmer auf die Stan- dardausgabe und ben¨otigt keinen File Pointer.

ohne stdout

. . . .

22 * zu viel Fast alle Funktionen ausstdio.hben¨otigen einen Fi- le Pointer, ohne ihn zu dereferenzieren.

fp

. . . .

24 Falsche Funktion

Um etwas auf die Standardfehlerausgabe zu schrei- ben, ben¨otigt man die Funktionfprintf()nebst des Parameters stderr.

fprintf()

Programm mit Korrekturen:

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 ( fp ) ) != 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 F I L E * fp ;

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

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

19 {

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

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

22 f c l o s e ( fp ) ;

23 }

(9)

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?

1. catschreibt immer auf die Standardausgabe.

(10)

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? Eine Ein- und eine Ausgabedatei Welchen Typ haben die beiden Parameter? FILE *

Welchen Typ sollte die Funktion haben? Beispielsweise int

Was f¨ur ein R¨uckgabewert ist sinnvoll? Bspw.die Zahl der kopierten Zeichen 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 : Entwickle eine Funktion zum Kopieren einer Datei.

Parameter : Je ein Zeiger auf die Eingabe- und Ausgabedatei, die bereits ge¨offnet sind.

R¨uckgabewert : Die Zahl der kopierten Zeichen.

Schleifenbedingung : Lesen bis das Dateiende (EOF) erreicht ist.

(11)

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 . . .

(12)

4. Implementierung

Das Kopieren einer einzigen Datei sollte nach den ganzen Vor¨uberlegnungen eigent- lich kein Problem mehr sein. Die zu entwickelnde Funktion bekommt die beiden File Pointer und liest solange von der Eingabe, bis dort das Dateiende erreicht wurde.

Jedes gelesene Zeichen muss auch ausgegeben werden. Parallel dazu muss f¨ur jedes Zeichen der Zeichenz¨ahler um eins erh¨oht werden.

Kopieren einer Datei

Parameter: Pointer to FILE: fin, fout Variablen: Integer: c, counter

setze counter = 0

lese ein zeichen von fin solange zeichen 6= EOF

wiederhole schreibe zeichen auf fout lese ein zeichen von fin setzte counter = counter + 1 return counter

5. Kodierung

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

2

3 int c o p y F i l e ( F I L E * fin , F I L E * f o u t )

4 {

5 int c , cnt ;

6 c = f g e t c ( fin ) ; // e r s t e s z e i c h e n l e s e n

7 for ( cnt = 0; c != EOF ; cnt ++ )

8 {

9 f p u t c ( c , f o u t ) ; // z e i c h e n s c h r e i b e n 10 c = f g e t c ( fin ) ; // n e u e s z e i c h e n l e s e n

11 }

12 r e t u r n cnt ; // z a h l der k o p i e r t e n z e i c h e n

13 }

In der Literatur kopiert man Dateien meist mit folgender (verk¨urzten) Sequenz:

1 w h i l e (( c = f g e t c ( fin ) ) != EOF )

2 f p u t c ( c , f o u t ) ;

Hier wird im Schleifenkopf das n¨achste Zeichen gelesen c = fgetc( fin ) und das Ergebnis der Zuweisung sogleich auf EOF gepr¨uft. Diese Variante arbeitet nat¨urlich genauso gut. Nur m¨ussen wir jetzt noch das Z¨ahlen der kopierten Zeichen integrieren,

(13)

1 int c o p y F i l e ( F I L E * fin , F I L E * f o u t )

2 {

3 int c , cnt ;

4 for ( cnt = 0; ( c = f g e t c ( fin ) ) != EOF ; cnt ++ )

5 f p u t c ( c , f o u t ) ;

6 r e t u r n cnt ;

7 }

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 : Entwicklung eines Hauptprogramms, das die FunktioncopyFile()mit den richtigen Parametern aufruft.

Ausgabe : Das Programm schreibt immer auf die Standardausgabe.

Parameter : Die einzelnen Kommandozeilenargumente

Sonderf¨alle : Falls keine Parameter angegeben werden, wird von der Tastaur gelesen.

Selbiges wenn der Dateiname "-"lautet.

3. Implementierung

Bei der Implementierung m¨ussen wir nur die entsprechenden F¨alle ber¨ucksichtigen:

Hauptprogramm: Kopieren von Dateien

wenn Parameter vorhanden sind: argc > 1 dann f¨ur alle Argumente

wiederhole wenn Dateiname 6= "-"

dann ¨offnen der Datei

falls kein Fehler auftrat

dann copyFile( file, stdout ) sonst Fehlermeldung ausgeben sonst copyFile( stdin, stdout )

sonst copyFile( stdin, stdout )

4. Kodierung

(14)

Unser fertiges Programm sieht wie folgt aus:

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

2

3 int c o p y F i l e ( F I L E * fin , F I L E * f o u t )

4 {

5 int c , cnt ;

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

7 f p u t c ( c , f o u t ) ;

8 r e t u r n cnt ;

9 }

10

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

12 {

13 int i ;

14 F I L E * fin ;

15 if ( a r g c > 1 )

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

17 if ( s t r c m p ( a r g v [ i ] , " - " ) )

18 if ( fin = f o p e n ( a r g v [ i ] , " r " ) )

19 {

20 c o p y F i l e ( fin , s t d o u t ) ;

21 f c l o s e ( fin ) ;

22 }

23 e l s e f p r i n t f ( stderr , " ’% s ’ k o n n t e n i c h t g e o e f f n e t w e r d e n \ n " , a r g v [ i ] ) ; 24 e l s e c o p y F i l e ( stdin , s t d o u t ) ;

25 e l s e c o p y F i l e ( stdin , s t d o u t ) ;

26 r e t u r n 0;

27 }

5. Testdaten

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

Referenzen

ÄHNLICHE DOKUMENTE

Jahre spart, Dann kann er Dann kann er sich einen sich einen Porsche Porsche kaufen kaufen. Wenn Wenn ( ( Bedingung Bedingung ; ; Dann_Wert Dann_Wert ; ;

Verwenden Sie die Funktion zur Anwendung des Leslie-Modells mit den demographi- schen Raten des Jahres 2006 und führen Sie eine Projektion durch, die mindestens 200

• Gehe in den Pfad: C:\Programme (x86)\Filius\ und nenne die Dateitypen aller im Ordner enthaltenen Dateien. Lasse dir dazu die Dateierweiterungen im

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

* Werden Vektoren unterschiedlicher l¨ ange ¨ ubergeben, so soll eine aussagekr¨ aftige Fehlermeldung die Folge seien.. Hinweis: Die mit * gekennzeichneten Punkte

Aufgabe 2: Massenpunkt auf dem Kegelmantel (8 Punkte) Eine Punktmasse bewegt sich reibungsfrei und unter Einfluss der Graviation auf der Innenseite eines Kegelmantels mit

Zeigen Sie, dass das nicht

Zeigen Sie, dass sinh eine steng monotone Bijektion auf R ist, und dass seine Umkehrfunktion arsinh (Areasinus Hyperbolicus)