0 Daumen
193 Aufrufe

Aufgabe:

Schreibt ein C++-Programmkopfstand, das eine Datei einliest und in exakt byteweise um-gekehrter Reihenfolge wieder ausgibt, d. h. das letzte Byte zuerst und das erste zuletzt. Die Eingabedatei wird als erster Parameter angegeben. Der zweite gibt die Ausgabedatei an, die ggf. neu anzulegen ist.

Ein Aufruf des Programms könnte z. B. lauten: kopfstand eingabeausgabe

Euer Programm soll mit beliebig langen Eingabedateien umgehen können (d. h. Ihrkönnt nicht einfach die gesamte Datei in den Speicher laden, sondern müsst dieseblockweisebearbeiten). Euer Programm soll möglichst effizient arbeiten.

Begründet, warum Eure Implementierungeffizient ist bzw. welche Faktoren Ihr hierbei bedacht habt. Ihr könnt zum Beispielmmap() verwenden, um jeweils einen Dateiblock in den Hauptspeicher einzublenden (wieder ausblendennicht vergessen!).

Alternativ könnt Ihr die Methoden read(), write() und seekg() / tellg() der jeweils eingesetzten I/O-Stream-Objekte nutzen, um auf die Eingabedatei und auf die Ausgabedatei zuzugreifen. Welche Variante ist schneller? Welche Betriebssystem-Funktionalitätenbeeinflussen eventuell das Messergebnis?

Erkennt und behandelt Fehler. Stellt geeignete Testfälle vor. (Kann Euer Programm mit einerleeren Eingabedatei umgehen? Kann Euer Programm mit einer Eingabedatei umgehen, die eine ungerade Länge hat? Kommt es mit Nullbytes in den Nutzdaten zurecht?)

Für die Messung der Laufzeiten soll das Programm auf der Kommandozeile mit dem Hilfsprogramm time ausgeführt werden.

Avatar von

1 Antwort

0 Daumen

Antwort mit KI erzeugt

Programmimplementierung

Zur Lösung der Aufgabe haben wir uns für die Nutzung der Methoden read(), write() sowie seekg() / tellg() entschieden. Diese Methoden erlauben es, die Datei in Blöcken zu lesen und zu schreiben sowie die Position innerhalb der Datei zu kontrollieren, ohne den Inhalt vollständig in den Speicher zu laden. Dies ist vor allem bei großen Dateien von entscheidender Bedeutung für die Effizienz des Programms.

Warum ist diese Implementierung effizient?

1. Speicherbedarf: Da die Datei in Blöcken gelesen und verarbeitet wird, bleibt der Speicherbedarf unabhängig von der Dateigröße konstant. Dies verhindert Speicherüberläufe und macht den Ansatz skalierbar.

2. I/O-Operationen: Durch den blockweisen Zugriff werden unnötige I/O-Operationen minimiert. Da I/O-Zugriffe im Vergleich zu Rechenoperationen langsam sind, führt dies zu einer erheblichen Leistungssteigerung, besonders bei großen Dateien.

3. Flexibilität bei Dateigrößen: Der Ansatz kann effizient mit jeder Dateigröße umgehen, seien es sehr kleine oder außerordentlich große Dateien, ohne dabei in Leistung oder Funktionalität eingeschränkt zu sein.

C++ Code-Baustein

cpp
#include <fstream>
#include <iostream>
#include <vector>

int main(int argc, char* argv[]) {
    if(argc != 3) {
        std::cerr << "Benutzung: " << argv[0] << " <Eingabedatei> <Ausgabedatei>\n";
        return 1;
    }

    std::ifstream inputFile(argv[1], std::ios::binary);
    std::ofstream outputFile(argv[2], std::ios::binary | std::ios::trunc);

    if(!inputFile) {
        std::cerr << "Fehler beim Öffnen der Eingabedatei.\n";
        return 1;
    }

    if(!outputFile) {
        std::cerr << "Fehler beim Anlegen der Ausgabedatei.\n";
        return 1;
    }

    inputFile.seekg(0, inputFile.end);
    long fileSize = inputFile.tellg();
    inputFile.seekg(0, inputFile.beg);

    const size_t bufferSize = 1024; // Größe des Lese/Schreibblocks
    std::vector<char> buffer(bufferSize);
    long position = fileSize - bufferSize;

    while(position >= 0) {
        inputFile.seekg(position);
        inputFile.read(buffer.data(), bufferSize);
        outputFile.write(buffer.data(), inputFile.gcount());
        position -= bufferSize;
    }

    if (position < 0 && position + bufferSize != 0) {
        // Bearbeitung der letzten, unvollständigen, Dateisektion
        inputFile.seekg(0);
        inputFile.read(buffer.data(), fileSize % bufferSize);
        outputFile.write(buffer.data(), inputFile.gcount());
    }

    return 0;
}


Faktoren für Effizienz und Leistungsmessungen

- Betriebssystem-Caching: Abhängig vom Betriebssystem kann das Ergebnis beeinflusst werden. Betriebssysteme verwenden Mechanismen wie Dateisystem-Caching, welche die Performance beeinflussen können.
- Hardware: Die Geschwindigkeit der Festplatte oder des Speichermediums kann ebenfalls einen erheblichen Einfluss auf die Laufzeitmessung haben.
- Vergleich mit mmap(): Die mmap()-Funktion könnte in einigen Fällen schneller sein, da sie direkten Speicherzugang ermöglicht. Ihr Vorteil wird besonders bei Dateien sichtbar, die vollständig in den Speicher passen. Jedoch ist die hier vorgeschlagene blockbasierte Methode universeller und ermöglicht eine feinere Kontrolle über die Speichernutzung und Fehlerbehandlung.

Erkennung und Behandlung von Fehlern

Der implementierte Code prüft auf Fehler beim Öffnen der Dateien und liest/schreibt Daten in festgelegten Blöcken, wodurch potenzielle Fehler während des Lese-/Schreibvorgangs minimiert werden. Fehler wie eine leere Eingabedatei oder unerwartete Dateiinhalte (z.B. Nullbytes) werden durch das robuste Design gehandhabt, das sicherstellt, dass jeder Block korrekt verarbeitet wird, unabhängig vom spezifischen Inhalt.

Testfälle

- Leere Datei: Das Programm sollte erkennen, dass die Datei leer ist und eine leere Ausgabedatei erstellen.
- Ungerade Dateilänge: Das Programm sollte korrekt mit Dateien umgehen, deren Längen nicht durch die Blockgröße teilbar sind, indem es die restlichen Bytes korrekt liest und schreibt.
- Nullbytes in Daten: Das Programm sollte in der Lage sein, Nullbytes ohne Probleme zu verarbeiten, da es binäre Daten behandelt und keine Annahmen über den Inhalt macht.
Avatar von

Ein anderes Problem?

Stell deine Frage

Willkommen bei der Stacklounge! Stell deine Frage einfach und kostenlos

x
Made by a lovely community