Allgemeine Fragen zu Programmierübungen

coder111

Mitglied
Hallo,
dieses Programm soll eine Datei einlesen und den Inhalt hexadezimal ausgeben.
Ab der Zeile 33 verstehe ich nicht mehr den Code. Könntet ihr mir das näher erläutern?


C++:
/* cppbuch/loesungen/k1/einausgabe/16.cpp
   Beispiel zum Buch von U. Breymann: Der C++ Programmierer; 4. Aufl., korr. Nachdruck 2016
   Diese Software ist freie Software. Website: http://www.cppbuch.de/
*/
//  Datei hexadezimal ausgeben
// Hilfsmittel: Zeichen mit String ermitteln
#include <iostream>
#include <cstdlib> // für exit()
#include <string>
#include <fstream>
using namespace std;

int main() {
  cout << "Dateiname :";
  string Quelldateiname;
  cin >> Quelldateiname;

  ifstream quelle(Quelldateiname, ios::binary);
  if (!quelle) { // muss existieren
    cerr << Quelldateiname << " kann nicht geöffnet werden!\n";
    exit(-1);
  }

  // char ist notwendig, weil get(unsigned char&) nicht
  // implementiert ist (GNU C++)
  char c; // signed!
  unsigned int count = 0;
  while (quelle.get(c)) {
    unsigned char uc = static_cast<unsigned char>(c);
    unsigned int zahl = static_cast<unsigned int>(uc);
    const string codierung{"0123456789ABCDEF"};
    // 2 stellig ausgeben
    cout << codierung.at(zahl / 16) << codierung.at(zahl % 16) << ' ';
    if (++count % 20 == 0) {
      cout << '\n';
    }
  }
  cout << '\n';
}

----------------------------------------------------------------------------------------------------------------

Und eine weitere Frage hätte ich noch beim Arbeiten mit Dateien.
Wenn ich folgenden Code implementiere, dann müsste auf meinem Computer eine Datei mit dem Namen <Quelldateiname> existieren. Wo finde ich diese Datei?

C++:
cout << "Dateiname :";
  string Quelldateiname;
  cin >> Quelldateiname;

  ifstream quelle(Quelldateiname, ios::binary);
 

cwriter

Erfahrenes Mitglied
char ist notwendig, weil get(unsigned char&) nicht implementiert ist (GNU C++)
Ja und nein.
Man könnte auch direkt bei der Übergabe von uchar nach char casten und dann mit uchar weiterarbeiten.
Das Casting über uc nach zahl ist überflüssig. Man kann direkt uc verwenden.

Zu deiner Frage:
Das hexadezimale System funktioniert wie jedes System mit einer Basis. Hier ist diese 16 (griech. hexadecim).
Daher gilt bei bspw. 42d:
42d = 4*10^1+2*10^0 = 40d + 2d.
Aber: 42h = 4*16^1 + 2*16^0 = 64d +2d = 66d.
Da nur byteweise gelesen wird, musst du nur 2 Werte betrachten: Die 16er und die 1er (vgl. 10er und 1er im Dezimalsystem).
An die 16er kommt man, indem man durch 16 teilt, an die Einer, indem man den Rest der Division nimmt. Mehr bitte im Internet nachlesen, diese Aufgabe ist ein Klassiker.
Das Resultat ist dann die Stelle des Arrays, wo die Repräsentation steht.
Für Computer kann man es sich noch einfacher machen:
16 = 2^4.
Also ist die Rechnung für 2 hd-Stellen eigentlich: (Byte >> 4) für die erste Stelle (ein 4bit-Wert entsteht) und (Byte & 0xF) für die Einer.
Diese beiden logischen Operationen sind identisch zu deinen arithmetischen. (Warum?)

Zur 2. Frage:
Wenn ich folgenden Code implementiere, dann müsste auf meinem Computer eine Datei mit dem Namen <Quelldateiname> existieren. Wo finde ich diese Datei?
Wenn der Pfad relativ ist (also unter Unix kein '/' an erster Stelle hat, unter Windows nirgends ein ':'), dann es ist relativ zum Arbeitsverzeichnis (oft, aber nicht immer ist das auch das Programmverzeichnis).
Siehe auch argv[0] für das Programmverzeichnis und argv[1] für das Arbeitsverzeichnis.
Wird dein Programm bspw. in "/test/" ausgeführt, dann wird mit dem Pfad "test/t.txt" die Datei "/test/test/t.txt" geöffnet.
Ist der Pfad aber "/test/t.txt", dann wird die Datei "/test/t.txt" geöffnet


Gruss
cwriter
 

coder111

Mitglied
Die Antwort zu meiner ersten Frage (ab Zeile 33) verstehe ich immer noch nicht.
Was meinst du mit
"Da nur byteweise gelesen wird, musst du nur 2 Werte betrachten:"
 

sheel

I love Asm
Ein Byte hat 8 Bit und kann damit 2^8 also 256 Werte speichern. Von 0 bis 255. 255 hexadezimal ist FF. Ein Byte hexadezimal geschrieben kann also max. 2 Stellen haben, nie mehr.

Btw., wenn
Code:
char c; // signed!
so in dem Buch (oder der Webseite dazu) steht, besser tewas vorsichtig sein damit. ob "char" ohne genauere Angabe signed oder unsigned ist hängt vom Compiler etc. ab.

(Wenn man genau sein will ist es eigentlich auch falsch, in chars einzulesen und zu erwarten dass es (nur) Bytewerte sind... meistens stimmts, aber nicht immer)
 

coder111

Mitglied
Und wozu der counter?
Warum wird auf ++count % 20 == 0 geprüft?
Und warm die zwei \n. ? Eins müsste doch, für den Zeilenumbruch ausreichen.
C++:
if (++count % 20 == 0) {
      cout << '\n';
    }
  }
  cout << '\n';
 

cwriter

Erfahrenes Mitglied
Und wozu der counter?
Warum wird auf ++count % 20 == 0 geprüft?
Das sorgt nur dafür, dass alle 20 Bytes eine neue Zeile begonnen wird.
Modulo erstellt mathematische Äquivalenzklassen.
So sind für alle c in [0,20[, k in N: k*20 + c "gleichwertig". Also ist z.B. 41 mod20 1.
Auch '=' erzeugt Äquivalenzklassen, die aber je nur ein Element haben: Für alle k in N: [=] = {k}.
Das aber nur als Hintergrund.
In deinem Code ist das Modulo gleichwertig zu:
C:
if(++count == 20)
{
count = 0;
cout << newline;
}

Das 2. '\n' wird ganz am Ende ausgeführt, um am Schluss eine Abstandszeile zu generieren, auch wenn count mod 20 nicht gerade 0 ist.

Gruss
cwriter
 

coder111

Mitglied
Dank eurer Hilfe, ist es jetzt verständlicher :)

Nehmen wir an in einer Textdatei steht: "Das ist eine Textdatei!"
Wird dann jedes Zeichen (inklusive Leerzeichen und Sonderzeichen) genommen und ins Hexadezimalsystem umgewandelt?
 

sheel

I love Asm
Ja, inkl. Leerzeichen, Sonderzeichen usw.

Bzw., ob jedes "Zeichen" genommen wird ist nicht so eindeutig.
Sehr vereinfacht gesagt: Es gibt sowas wie Zeichensätze, die Buchstaben Nummern zuordnen, zB. 65 für das große A (weil Computer letztendlich nur Nummern speichern können. Warum man für das Byte mit Wert 65 ein A sieht liegt "nur" daran, dass es für jede bekannte Nummer Bilder gibt, die angezeigt werden können).
Für ein A in der Datei würde also also die Nummer 65 (also 65 im Zehnersystem) hexadezimal ausgegeben (ist 41).
Das "Problem" ist, es gibt auch sichtbare Zeichen, die mehr als ein Byte brauchen. Die 256 möglichen Bytewerte reichen nicht, weil es ja auch Griechisch, Russisch, Japanisch usw.usw. gibt, nicht nur A-Z und ein paar Satzzeichen.

Kurz, einfach nicht darauf verlassen, dass ein Buchstabe immer ein Byte sein muss.
(Zeichensätze sind ein eigenes großes Thema, nichts für jetzt)
 

cwriter

Erfahrenes Mitglied
Die Erweiterung zu der Aufgabe verstehe ich nicht. Ich weiß nicht, was ich da machen soll.
Du sollst jede Zeile (definiert als 16 Bytes) zweimal ausgeben, einmal als ASCII und einmal hexadezimal.
Dazu brauchst du einen 16 Bytes grossen Buffer, in den du einliest und dann musst du 2x über diesen Buffer iterieren und eine entsprechende Ausgabe machen.

Gruss
cwriter