Die Suche ergab 238 Treffer

von bbock
26.06.2023, 20:53
Forum: Programmierung
Thema: Fragen und Antworten zum C-Kurs
Antworten: 108
Zugriffe: 538628

Re: Fragen und Antworten zum C-Kurs

Paul hat geschrieben: 25.06.2023, 13:16 ...
Auch wenn der Kurs derzeit eingeschlafen ist.
...
Das mag am schönen Wetter liegen, der Kurs wird aber definitiv fortgesetzt. Es gibt noch mehrere Themen, die ich vorstellen möchte.
von bbock
26.06.2023, 20:48
Forum: Programmierung
Thema: Übungen zu "Programmieren in C mit dem z88dk"
Antworten: 11
Zugriffe: 57935

Re: Übungen zu "Programmieren in C mit dem z88dk"

Immerhin eine Lösung; die Verwendung der forms3-Bibliothek ist schon etwas für Fortgeschrittene.

Hier nun die Musterlösung mit der o.g. Eingabefunktion inputString:
lesson01.zip
(1.31 KiB) 1639-mal heruntergeladen

Kompiliert wird mit

Code: Alles auswählen

zcc +cpm -subtype=pcw80 lesson01.c -o lesson01.com
von bbock
23.06.2023, 17:45
Forum: Programmierung
Thema: HP-GL Interpreter
Antworten: 48
Zugriffe: 253648

Re: noch neun Bilder

ein Thüringer hat geschrieben: 23.06.2023, 09:22 ...
Bernd, wird es eine Diashow-Funktion wie für VECREAD auch für PBMREAD geben?
...
Das lässt sich schon machen, denke ich. Vielleicht auch noch weitere Optionen; es ist wohl vor allem eine Frage der Zeit. ;)
von bbock
27.05.2023, 19:54
Forum: Programmierung
Thema: Übungen zu "Programmieren in C mit dem z88dk"
Antworten: 11
Zugriffe: 57935

Re: Übungen zu "Programmieren in C mit dem z88dk"

Solltet ihr Schwierigkeiten haben die Eingabe benutzerfreundlich hinzubekommen, dann hilft euch vmtl. folgende Eingaberoutine:

Code: Alles auswählen

#include <stdio.h>
#include <conio.h>

#define CH_BEEP        7
#define CH_BACKSPACE   8
#define CH_RETURN     10
#define CH_AT         22
#define CH_SZ        126
#define CH_DEL_LEFT  127


/* Input a string with up to maxlen characters via keyboard.
   Parameters: cp      pointer to character buffer
               maxlen  maximum input length allowed
 */
void inputString(char *cp, int maxlen) {
    char key;
    char *ptrBuf;
    int numChars;
    
    ptrBuf = cp;
    *ptrBuf = '\0'; // we start with an empty string
    numChars = 0;
    
    for (;;) {
        key = getch();
        
        if (key == CH_RETURN) { // finish input if <return> pressed
            putchar('\n');
            break;
        }
        
        if (numChars < maxlen) {
            if (key >  31 && key < 126 || // regular characters
                key >  90 && key <  94 || // uppercase Umlaut
                key > 122 && key < 126 || // lowercase Umlaut
//              key == CH_AT ||
                key == CH_SZ)
            {
                putchar(key);    // print character on screen
                *ptrBuf++ = key; // write key to buffer
                *ptrBuf = '\0';  // finalise string and point to the '\0' character
                ++numChars;
            }
            else {
                if (key == CH_DEL_LEFT && *cp != '\0') { // delete left and characters available
                    putchar(CH_BACKSPACE); // move left
                    putchar(' ');          // overwrite with blank
                    putchar(CH_BACKSPACE); // move left again
                    *--ptrBuf = '\0';      // shortens the string by 1 char
                    --numChars;
                }
                else {
                    putchar(CH_BEEP); // invalid key typed => beep
                }
            }
        }
        else {
            putchar(CH_BEEP); // maximum number of characters reached => beep
        }
    }
}
von bbock
06.05.2023, 18:07
Forum: Programmierung
Thema: Fragen und Antworten zum C-Kurs
Antworten: 108
Zugriffe: 538628

Re: Fragen und Antworten zum C-Kurs

Die Übungen zum C-Kurs werden in diesem Thema gesammelt. Auch eure Fragen und Lösungen sollen dort eingetragen werden. Nach einer gewissen Zeit werde ich eine Musterlösung anhängen.

Ich bin gespannt auf eure Mitarbeit! :)
von bbock
06.05.2023, 18:03
Forum: Programmierung
Thema: Übungen zu "Programmieren in C mit dem z88dk"
Antworten: 11
Zugriffe: 57935

Übungen zu "Programmieren in C mit dem z88dk"

Übung 1

Der Benutzer soll die Adresse einer Person eingeben. Diese wird anschließend in einer Textdatei namens adressen.txt gespeichert. Nach dem Speichern erscheint eine Abfrage, ob eine weitere Adresse eingegeben werden soll. Wird die Abfrage bejaht, dann wird eine weitere Adresse eingegeben, die dann an die Textdatei adressen.txt angehängt wird. Wird sie verneint, dann wird das Programm beendet.

Einzugebende Felder:
Name
Vorname
Straße
PLZ
Wohnort
Telefon
E-Mail

Darstellung in der Textdatei (Beispiel):
Hugo Hirsch
Schloßallee 3
67118 Frankfurt
069 12345678
hugo.hirsch@pappnase.de
von bbock
15.04.2023, 19:06
Forum: Programmierung
Thema: Programmieren in C mit dem z88dk
Antworten: 20
Zugriffe: 106253

16. Arbeiten mit Dateien 3

Diesmal wollen wir eine C-Quelldatei bearbeiten: die Zeilenkommentare mit dem doppelten Schrägstrich sollen in gewöhnliche C-Kommentare mit Schrägstrich-Stern am Anfang und Stern-Schrägstrich am Ende des Kommentars umgewandelt werden, denn nicht alle C-Compiler unterstützen die Zeilenkommentare. Wie können wir dabei vorgehen?

Wir benötigen zwei Dateien: die Originaldatei mit den Zeilenkommentaren und die umgewandelte, die nur noch normale Kommentaren enthält. Die Originaldatei öffnen wir zum Lesen, die Ausgabedatei zum Schreiben. Wir lesen die Originaldatei Zeile für Zeile ein. Dann suchen wir nach "//". Finden wir die beiden Schrägstriche, dann ersetzen wir die beiden Zeichen durch "/*"; danach suchen wir das Zeilenende und fügen davor ein "*/" ein. Finden wir die beiden Schrägstriche nicht, dann schreiben wir die Zeile unverändert in die Ausgabedatei.

Wir beginnen mit ein paar Variablen in der Funktion main():

Code: Alles auswählen

#include <stdio.h>
#include <string.h>

#define FILENAMELENGTH  20
#define BUFSIZE        512

/* Main
 */
int main(void) {
    char filename[FILENAMELENGTH];
    char filenameOut[FILENAMELENGTH];
    FILE *fp;
    FILE *fpOut;
    char buf[BUFSIZE];
    char *cp;
    size_t len;
Wir brauchen die beiden char-Arrays für die Dateinamen, die wir während der Programmausführung über die Tastatur eingeben werden. Wir sehen eine Länge von 20 Zeichen vor, was für CP/M-Dateinamen ausreicht. Dann brauchen wir noch zwei FILE pointer und einen Puffer für eine eingelesene Zeile. Der Puffer ist mit 512 Zeichen ausreichend groß bemessen, dass man gängige C-Programme damit bearbeiten können sollte. Natürlich ist C nicht formatgebunden, d.h. man könnte sogar ein komplettes C-Programm in eine einzige Zeile schreiben. Für solche Fälle ist unser Programm nicht ausgelegt; in der Praxis sollten Zeilen mit mehr als 512 Zeichen eher nicht vorkommen. Schließlich haben wir noch einen char pointer und eine Längen-Variable vom Typ size_t. Die Typdeklaration von size_t finden wir im include-Verzeichnis des Compilers unter sys/types.h: size_t entspricht hier einem unsigned int. Variablen dieses Typs können also Werte im Bereich von 0 bis 65535 annehmen.

Es folgt die Eingabe der Dateinamen und das Öffnen der Dateien:

Code: Alles auswählen

    printf("Zu konvertierende Datei: ");
    scanf("%s", filename);
    
    fp = fopen(filename, "r");
    if (fp == NULL) {
        printf("Fehler: Datei %s nicht gefunden!\n", filename);
        return -1;
    }

    printf("Ausgabedatei: ");
    scanf("%s", filenameOut);
    
    fpOut = fopen(filenameOut, "w");
    if (fpOut == NULL) {
        printf("Fehler: Konnte Ausgabedatei %s nicht oeffnen!\n", filenameOut);
        fclose(fp);
        return -1;
    }
Wenn eine der Dateien nicht geöffnet werden kann, z.B. weil wir einen ungültigen Dateinamen eingegeben haben, dann liefert fopen() den Wert NULL zurück und wir geben eine Fehlermeldung aus und beenden das Programm mit dem Fehlercode -1.

Wir lesen der Zeilen in einer while-Schleife ein:

Code: Alles auswählen

    // Zeilen lesen
    while (fgets(buf, sizeof buf, fp) != NULL) {
Die bereits bekannte Funktion fgets() liest die Zeile in den Puffer buf ein und begrenzt das Einlesen sicherheitshalber auf die Größe des Puffers. Solange die Funktion einen Wert ungleich NULL zurückliefert, haben wir eine weitere Zeile einlesen können, die wir anschließend verarbeiten.

Zunächst ermitteln wir die Länge der Zeile:

Code: Alles auswählen

        len = strlen(buf);
Dann suchen wir den doppelten Schrägstrich mit der String-Funktion strstr(). Diese sucht in einem String nach dem Vorkommen eines Such-Strings. Wenn der Suchstring gefunden wird, dann liefert strstr() einen Pointer auf das erste gefundene Zeichen zurück, also den ersten Schrägstrich. Den wollen wir nicht verändern und inkrementieren den Pointer, damit er auf den zweiten Schrägstrich zeigt. Mit *cp = '*'; überschreiben wir den Schrägstrich mit einem Stern.

Code: Alles auswählen

        // doppelten Schraegstrich suchen
        cp = strstr(buf, "//");
        if (cp != NULL) {
            ++cp; // zeigt jetzt auf den zweiten Schraegstrich
            *cp = '*'; // "//" in "/*" umwandeln
Jetzt müssen wir noch das Zeilenende finden um dort die Kommentarende-Kennung */ einzufügen.

Code: Alles auswählen

            // Zeilenende suchen
            cp = strchr(cp, '\n');
            if (cp == NULL) {
                printf("Oh - das sollte nicht passieren: Zeilenende nicht gefunden!\n");
                return -1;
            }
            
            // Zeilenende mit " */" ueberschreiben
            *cp++ = ' ';
            *cp++ = '*';
            *cp   = '/';
Wir suchen das Newline-Zeichen mit der String-Funktion strchr(), da wir diesmal nur nach einem einzelnen Zeichen suchen wollen. Mit strstr() hätte man das auch machen können, allerdings wäre das weniger effizient.

Finden wir kein Newline-Zeichen, dann stimmt etwas ganz und gar nicht und wir steigen mit einer Fehlermeldung aus. Andernfalls überschreiben wir das Newline-Zeichen und die beiden folgenden Zeichen mit Leerzeichen, Stern und Schrägstricht, also " */". Damit zerstören wir das Zeilenende, das wir gleich aber wieder anhängen. Das tun wir in jedem Fall, also auch dann, wenn wir keinen doppelten Schrägstrich gefunden haben. Das ist nötig, weil fgets() nur das Newline-Zeichen einliest, nicht aber das Carriage-Return-Zeichen.

Vorher passen wir aber noch die Zeilenlänge an, die ja jetzt um drei eingefügte Zeichen angewachsen ist:

Code: Alles auswählen

            // Laenge anpassen: wir haben jetzt 3 Zeichen mehr
            len += 3;
        }
Jetzt schreiben wir das Zeilenende und das String-Abschlusszeichen in den Puffer und schreiben den Puffer mit der evtl. geänderte Zeile in die Ausgabedatei. Dazu verwenden wir die Funktion fwrite():

Code: Alles auswählen

        // CR, LF und String-Abschluss
        buf[--len] = '\r';
        buf[++len] = '\n';
        buf[++len] = '\0';
        
        // Zeile schreiben
        fwrite(buf, 1, len, fpOut);
Am Ende, also nach der Schleife, müssen wir noch die beiden Dateien schließen und liefern den Fehlercode 0 an das Betriebssystem zurück.

Code: Alles auswählen

    }

    fclose(fp);
    fclose(fpOut);

    return 0;
}
In file_io_3.zip befindet sich das komplette Programm file3.c und eine Quelldatei mit Zeilenkommentaren zum Ausprobieren (pointer1.c).

file_io_3.zip
file3.c und pointer1.c
(1.22 KiB) 2979-mal heruntergeladen

Es sei noch anzumerken, dass das Programm in dieser Form noch nicht perfekt ist. Die Limitierung auf eine Zeilenlänge von max. 512 Zeichen (eigentlich noch etwas weniger wegen der Zeilenende- und Stringende-Zeichen) wurde bereits erwähnt. Außerdem erkennt das Programm nicht, wenn sich die doppelten Schrägstriche in einem String-Literal befinden, d.h. wenn im Code so etwas wie s = "abc // xyz"; vorkommt, dann würde hier der doppelte Schrägstrich fälschlicherweise als Kommentar erkannt. Selbiges gilt für doppelte Schrägstriche in Präprozessor-Direktiven. Natürlich kann man das Programm erweitern, damit es solche Fälle erkennt, aber das würde hier den Rahmen sprengen.
von bbock
07.04.2023, 10:41
Forum: Programmierung
Thema: Fragen und Antworten zum C-Kurs
Antworten: 108
Zugriffe: 538628

Re: Fragen und Antworten zum C-Kurs

Paul hat geschrieben: 07.04.2023, 09:52 ...
Was geschieht mit buf? Wird beim Aufruf der gesamte Buffer kopiert? Das wäre dann ja vergleichsweise langsam, erklärt aber warum der buf vom main nicht verändert werden kann.
Ist es dann sinnvoll die resultierende Variable mit dem gleichen Namen zu versehen? Intuitiv hätte ich lbuf verwendet was eine lokale Kopie von buf andeutet.
In Kapitel 10.2 habe ich geschrieben, dass der Name eines Arrays auch ein Pointer ist. buf ist also nicht der Wert des Arrays mit all seinen 100 Characters, sondern der Pointer, der auf den ersten Character zeigt. buf ist also eine Adresse, nicht der ganze Array. Beim Aufruf einer Funktion mit buf als Parameter wird also nicht der ganze Buffer kopiert, sondern nur die zwei Bytes des Zeigers.
Paul hat geschrieben: 07.04.2023, 09:52 Bei Parsetoken wiederum übergibst du die lokale Kopie von buf und gibst ihr den Namen start, was ja wiederum eine lokale kopie von buf ist, genauso wie field und delimiter.
Es wird die Adresse des Arrays übergeben, keine Kopie. Die Änderungen mit *start=... in parseToken ändern den Inhalt des Arrays buf in main.
Paul hat geschrieben: 07.04.2023, 09:52 du veränderst start, was erstmal keine Auswirkung hat weil es ja eine lokale Kopie ist. Am ende erhöhst du noch start um eins und gibst es als rückgabewert zurück.
Spannender wird es mit field. Das veränderst du und gibst am ende noch das Stringende Zeichen hinzu. Aber ist es nicht genau wie start eine lokale Kopie?
Delimiter wird nicht verändert, also stellt sich auch keine Frage bezüglich der Auswirkung auf die aufrufende Parseline.
Diese Fragen sollten mit o.g. Erklärungen beantwortet sein.
von bbock
07.04.2023, 10:30
Forum: Programmierung
Thema: Fragen und Antworten zum C-Kurs
Antworten: 108
Zugriffe: 538628

Re: Fragen und Antworten zum C-Kurs

Paul hat geschrieben: 07.04.2023, 09:43 ...
- die Größe des Buffers. Du wählst 100, die längste Zeile ist 86. Man könnte auch 255 verwenden. Aber was ist mit Buffer von 20? Wie kann ich in Etappen einlesen wenn mein Puffer kleiner ist als eine ganze Zeile?
Ja, man könnte auch 255 verwenden. Oder 2000. Wenn man mit festen Feldgrößen arbeitet, muss man immer überlegen, wie groß der zu speichernde Inhalt eigentlich sein kann. Der Buffer muss groß genug sein, damit die längste Zeile hineinpasst, oder man muss vorsehen eine Zeile in mehreren Etappen einzulesen, wie du das nennst. Das macht das Einlesen komplizierter, weil man im ersten Anlauf nicht alle Felder bekommt und Felder über zwei Lesevorgänge geteilt sein können.
Ist die maximale Länge einer Zeile bekannt, z.B. weil man weiß, aus welcher Quelle die CSV-Datei stammt, dann kann man den Buffer entsprechend dimensionieren. Man muss aber beachten, dass man auch Platz für den Zeilenumbruch und das String-Ende-Zeichen vorsieht.
Paul hat geschrieben: 07.04.2023, 09:43 - Die Größe der t_line objekte. Für den Staat reservierst du 60, für die Stadt 50, sind zusammen 110, mehr als jede Zeile.
Das sind für die Programmierung wichtige Werte aber woher bekomme ich die?
Die 60 und die 50 sind zusammen zwar mehr als jede Zeile lang ist, aber wir haben ja viele Zeilen, und so kann in einer Staat besonders lang sein und in einer anderen die Stadt. Man muss jeweils genug Platz für das längste Feld reservieren.
von bbock
31.03.2023, 13:53
Forum: Programmierung
Thema: Fragen und Antworten zum C-Kurs
Antworten: 108
Zugriffe: 538628

Re: Fragen und Antworten zum C-Kurs

Ja, Kurt, da gibt's noch so einiges... ;)

Was ist denn mit schombi? Ist der noch dabei?