Forum: PC-Programmierung Warum funktioniert mein c++ stream iterator basiertes cat nicht?


Announcement: there is an English version of this forum on EmbDev.net. Posts you create there will be displayed on Mikrocontroller.net and EmbDev.net.
von Z. Klatscher (Gast)


Bewertung
0 lesenswert
nicht lesenswert
Liebe Leute, was muss ich ändern, damit mein kleines cat Programm so 
geht wie das von GNU coreutils 8.31? Ich zeigs euch
#include <algorithm>
#include <fstream>
#include <iostream>
#include <iterator>
using namespace std;
int main (int argc, char** argv) {
        if (1 == argc) {
                copy (istream_iterator <char> (cin)
                                , istream_iterator <char> ()
                                , ostream_iterator <char> (cout));
        } else {
                for (size_t n = 1; n < argc; ++n) {
                        auto a = ifstream (argv [n]);
                        copy (istream_iterator <char> (a)
                                        , istream_iterator <char> ()
                                        , ostream_iterator <char> (cout));
                }
        }
        return 0;
}
soll so machen
% cat
bla
bla
blub
blub
blib
blib
macht aber
./cat
bla
blablub
blubblib
blib
Am liebsten mit Iteratoren und auch mit C glücklich. Besten Dank

von zer0@away (Gast)


Bewertung
0 lesenswert
nicht lesenswert
Vorsichtige Behauptung: Mit den streams wirst du da nicht so einfach 
glücklich. Die Dinger sind für formatierte Text-Operationen gedacht. Das 
heißt, dass alles was gelesen wird, erstmal durch die Mangel gedreht 
wird und irgendwelche Character-Encodings aus der locale angewendet 
werden usw.

Um eine Datei garantiert unverändert einzulesen müsstest du schon mit 
ios::binary und istream::read arbeiten oder ein Guru bezüglich der Spec 
sein, die da zB sagt
When reading characters, std::istream_iterator skips whitespace by default (unless disabled with std::noskipws or equivalent), while std::istreambuf_iterator does not. In addition, std::istreambuf_iterator is more efficient, since it avoids the overhead of constructing and destructing the sentry object once per character.
Ich würde mich nicht darauf verlassen, dass ein Text-Stream unverändert 
ankommt.
https://en.cppreference.com/w/cpp/io/c#Binary_and_text_modes

Um den binary mode auf cin (stdin) zu setzen, hilft vielleicht
freopen(0, "rb", stdin);
Aber das würde ich an deiner Stelle noch einmal prüfen.

von Wilhelm M. (wimalopaan)


Bewertung
0 lesenswert
nicht lesenswert
Du brauchst einen istreambuf_iterator<char>.

von zer0@away (Gast)


Bewertung
0 lesenswert
nicht lesenswert
Wilhelm M. schrieb:
> Du brauchst einen istreambuf_iterator<char>.

Auf welchen Teil der Spec beziehst du dich da, dass der streambuf 
garantiert "unverfälschte" Daten enthält?
Ich lese da
The character representation and encoding in the controlled sequence may be different from the character representations in the associated sequence

Im Speziellen
Data read in from a text stream is guaranteed to compare equal to the data that were earlier written out to that stream only if all of the following is true: ...
A binary stream is an ordered sequence of characters that can transparently record internal data. Data read in from a binary stream always equals to the data that were earlier written out to that stream. Implementations are only allowed to append a number of null characters to the end of the stream.

Ist da garantiert, dass diese Nullen nicht schon im Buffer auftauchen?

von Z. Klatscher (Gast)


Angehängte Dateien:

Bewertung
0 lesenswert
nicht lesenswert
zer0@away schrieb:
> Vorsichtige Behauptung: Mit den streams wirst du da nicht so einfach
> glücklich.

Danke für deine Antwort. Jetzt hab ichs mit unformatiertem input. Das 
müsste ja jetzt auch im Textmode gehen und das lineending der Platform 
nehmen?

cat.cpp
#include <fstream>
#include <iostream>
#include <string>
using namespace std;
int main (int argc, char** argv) {
        if (1 == argc) {
                for (string s; getline (cin, s);) {
                        cout << s << endl;
                }
        } else {
                for (size_t n = 1; n < argc; ++n) {
                        auto a = ifstream (argv [n]);
                        for (string s; getline (a, s);) {
                                cout << s << endl;
                        }
                }
        }
        return 0;
}

Falls es jemand braucht, im Anhang cat.exe für Windows 32.

von Yalu X. (yalu) (Moderator)


Bewertung
0 lesenswert
nicht lesenswert
Beim Lesen eines char über einen input_iterator wird sämtlicher
Whitespace übersprungen.

Die Verwendung von getline kann Probleme bei überlangen Textzeilen
machen.

Ich würde es so machen:

#include <fstream>
#include <iostream>

using namespace std;

int main (int argc, char **argv) {
  if (argc == 1)
    cout << cin.rdbuf();
  else {
    for (int n = 1; n < argc; ++n)
      cout << ifstream(argv[n]).rdbuf();
  }
  return 0;
}

: Bearbeitet durch Moderator
von Heiko L. (zer0)


Bewertung
0 lesenswert
nicht lesenswert
Z. Klatscher schrieb:
> Das
> müsste ja jetzt auch im Textmode gehen und das lineending der Platform
> nehmen?

Die Frage wäre, ob du das willst:
On systems like MS-DOS that distinguish between text and binary files, cat normally reads and writes in binary mode. However, cat reads in text mode if one of the options -bensAE is used or if cat is reading from standard input and standard input is a terminal. 
https://www.gnu.org/software/coreutils/manual/html_node/cat-invocation.html#cat-invocation

Das normale Verhalten ist, die Datei unverändert zu lassen. Man weiß 
nie, auf welcher Platform welche Datei geschrieben wurde und auch nicht, 
ob es eine Binärdatei ist. Das resultierende Chaos sieht man immer, 
wenn man Dateien zwischen Posix und Windows hin- und herschiebt.
Ich würde ein cat, dass per Default Dateien verändert, nicht gerade 
begrüßen... >:

von Rolf M. (rmagnus)


Bewertung
0 lesenswert
nicht lesenswert
Heiko L. schrieb:
> Ich würde ein cat, dass per Default Dateien verändert, nicht gerade
> begrüßen... >:

Ich auch nicht. Die Hauptaufgabe von cat (wenn auch oft für anderes 
benutzt) ist ja eigentlich, mehrere Dateien zu einer zusammenzufügen 
(conCATenate). Und das sollte natürlich nicht nur mit Text-, sondern 
auch mit Binärdateien sauber funktionieren.

von Wilhelm M. (wimalopaan)


Bewertung
-1 lesenswert
nicht lesenswert
Z. Klatscher schrieb:
> Falls es jemand braucht, im Anhang cat.exe für Windows 32.

Ich hoffe, es benutzt niemand, denn es ist falsch!

Hier wird immer(!) ein endl am Ende der Datei angefügt, auch wenn in der 
Ursprungsdatei keins drin ist. Die Datei wird also in diesem Fall um 1 
Byte länger.

Richtig sind:

Beitrag "Re: Warum funktioniert mein c++ stream iterator basiertes cat nicht?"

oder

Beitrag "Re: Warum funktioniert mein c++ stream iterator basiertes cat nicht?"

von DPA (Gast)


Bewertung
0 lesenswert
nicht lesenswert
Wozu das ganze? Cat gibt es doch schon, wenn du es brauchst, nimm doch 
ein bestehendes. Unter windows kann man vergleichbares zeug über ein 
paar Umwege übrigens auch mit nativen Tools machen.

Dateien zusammensetzen, cat.bat:
@ECHO OFF
:Loop
IF "%1"=="" GOTO Continue
   type "%1"
SHIFT
GOTO Loop
:Continue
cat.bat a.txt b.txt

Consolen Eingabe auf console ausgeben: (nicht das selbe wie stdin, 
windows hat nichts äquivalentes zu /dev/stdin bzw. /dev/fd/0 btw. 
/proc/self/fd/0, geht also nicht mit dem Output des Vorgängerprogramms.)
type CON

Klar, unter Windows ist das alles etwas Komplexer, zum einfachen 
Scripting echt nicht zu gebrauchen, aber wenn man was richtiges will, 
muss man halt auch das echte & gute zeug nehmen, statt den Windows mist.

Antwort schreiben

Die Angabe einer E-Mail-Adresse ist freiwillig. Wenn Sie automatisch per E-Mail über Antworten auf Ihren Beitrag informiert werden möchten, melden Sie sich bitte an.

Wichtige Regeln - erst lesen, dann posten!

  • Groß- und Kleinschreibung verwenden
  • Längeren Sourcecode nicht im Text einfügen, sondern als Dateianhang

Formatierung (mehr Informationen...)

  • [c]C-Code[/c]
  • [avrasm]AVR-Assembler-Code[/avrasm]
  • [code]Code in anderen Sprachen, ASCII-Zeichnungen[/code]
  • [math]Formel in LaTeX-Syntax[/math]
  • [[Titel]] - Link zu Artikel
  • Verweis auf anderen Beitrag einfügen: Rechtsklick auf Beitragstitel,
    "Adresse kopieren", und in den Text einfügen




Bild automatisch verkleinern, falls nötig
Bitte das JPG-Format nur für Fotos und Scans verwenden!
Zeichnungen und Screenshots im PNG- oder
GIF-Format hochladen. Siehe Bildformate.

Mit dem Abschicken bestätigst du, die Nutzungsbedingungen anzuerkennen.