Ich möchte die neu eröffnen stdin und stdout (und vielleicht stderr wo ich gerade dabei bin) Dateihandles, damit zukünftige Aufrufe an printf() oder putchar() oder puts() wird in eine Datei gehen, und zukünftige Aufrufe an getc() und solche werden aus einer Datei kommen.
1) Ich möchte die Standardeingabe/-ausgabe/-fehler nicht dauerhaft verlieren. Vielleicht möchte ich sie später im Programm wiederverwenden.
2) Ich möchte keine neuen Dateihandles öffnen, weil diese Dateihandles entweder viel herumgereicht werden müssten oder global (schaudern).
3) Ich möchte keine verwenden open() oder fork() oder andere systemabhängige Funktionen, wenn ich nicht anders kann.
Also im Grunde funktioniert es, dies zu tun:
stdin = fopen("newin", "r");
Und wenn ja, wie bekomme ich den ursprünglichen Wert von stdin der Rücken? Muss ich es in einem speichern FILE * und später wieder zurück?
Beachten Sie, dass stdin, stdoutund stderr sind globale Variablen.
– Alexis Wilke
22. Januar 2018 um 6:59 Uhr
bk1e
Warum verwenden freopen()? Die C89-Spezifikation enthält die Antwort in einer der Endnoten für den Abschnitt über <stdio.h>:
116. Die primäre Verwendung der freopen Die Funktion besteht darin, die mit einem Standardtextstream verknüpfte Datei zu ändern (stderr, stdinoder stdout), da diese Bezeichner keine änderbaren Lvalues sein müssen, denen der von der zurückgegebene Wert entspricht fopen Funktion zugeordnet werden kann.
freopen wird häufig missbraucht, z stdin = freopen("newin", "r", stdin);. Dies ist nicht mehr tragbar als fclose(stdin); stdin = fopen("newin", "r");. Beide Ausdrücke versuchen zuzuordnen stdinderen Zuweisung nicht garantiert ist.
Die richtige Art zu verwenden freopen ist die Zuordnung wegzulassen: freopen("newin", "r", stdin);
@AbiusX Ich bin mir nicht sicher, wie portabel das ist, aber unter POSIX-Umgebungen können Sie dies auf andere Weise tun – indem Sie die Bedeutung des zugrunde liegenden Handles mit ändern dup2(): linux.die.net/man/3/fflush. Auf diese Weise können Sie das STDOUT-Handle duplizieren, bevor Sie es ersetzen, und dann zurückkopieren. Denken Sie daran fflush(). Hier ist ein Beispiel dafür, wie dies zum Forken verwendet wird: stackoverflow.com/questions/9405985/…
– Philip Couling
7. Oktober 2014 um 9:40 Uhr
Für diejenigen, die den Standarddeskriptor schließen möchten, bevor er ihn erneut öffnet, gibt es eine in msvc2015 UCRT getestete Lösung, use sequence of fclose+_close+freopen: const int stdin_fileno = _fileno(stdin); fclose(stdin); if (stdin_fileno < 0) _close(STDIN_FILENO); /* may reallocate console here */; freopen("CONIN$", "r", stdin); /* use _get_osfhandle + SetStdHandle for not console application */. Das hat Sinn, weil freopen verwendet einen bereits zugewiesenen Deskriptor nicht wieder und weist einen neuen zu. Um es also zu zwingen, den Deskriptor wiederzuverwenden, müssen Sie es zuerst schließen.
Gibt es einen Unterschied zwischen stdin = freopen(“newin”, “r”, stdin); und fclose(stdin); stdin = fopen(“newin”, “r”); oder Nein?
– Chris Lutz
25. Februar 2009 um 6:02 Uhr
fopen öffnet ein Dateiobjekt, während freopen Ihren Datenstrom auf das Dateiobjekt zeigt. Von der Seite „Diese Funktion ist besonders nützlich, um vordefinierte Streams wie stdin, stdout und stderr auf bestimmte Dateien umzuleiten“
– Johannes T
25. Februar 2009 um 6:11 Uhr
Gibt es eine Möglichkeit, stdin/stdout mit ihren ursprünglichen Werten außer stdin = freopen(“/dev/tty”, “r”); oder so?
– Chris Lutz
25. Februar 2009 um 6:25 Uhr
Sie verwenden fclose mit dem Stream, auf den Sie auf die Datei verwiesen haben. fclose(stdout);
– Johannes T
25. Februar 2009 um 6:32 Uhr
Klarstellung: Syntaktische Zuweisung an stdinwie in stdin = fopen(...) oder stdin = freopen(...)ist nicht portabel, weil stdin darf ein Makro sein. stdio.h könnte enthalten #define stdin __builtin_get_stdin(), und natürlich können Sie das nicht auf die linke Seite eines Auftrags schreiben. @ChrisLutz, die richtige Art zu verwenden freopen ist einfach if (freopen("newin", "r", stdin) != NULL) ... — Der Rückgabewert ist nützlich, um anzuzeigen, ob das Öffnen fehlgeschlagen ist, aber Sie sollten ihn im Allgemeinen nichts zuweisen müssen.
– Quuxplusone
19. Dezember 2013 um 18:06 Uhr
Dies ist eine modifizierte Version der Methode von Tim Post; Ich habe /dev/tty anstelle von /dev/stdout verwendet. Ich weiß nicht, warum es nicht mit stdout funktioniert (was ein Link zu /proc/self/fd/1 ist):
Durch die Verwendung von /dev/tty wird die Ausgabe auf das Terminal umgeleitet, von dem aus die App gestartet wurde.
Hoffe, diese Informationen sind nützlich.
Tim Post
freopen("/my/newstdin", "r", stdin);
freopen("/my/newstdout", "w", stdout);
freopen("/my/newstderr", "w", stderr);
... do your stuff
freopen("/dev/stdin", "r", stdin);
...
...
Das spitzt die Nadel auf meinem Rund-Stift-Quadrat-Loch-O-Meter, was versuchst du zu erreichen?
Bearbeiten:
Denken Sie daran, dass stdin, stdout und stderr die Dateideskriptoren 0, 1 und 2 für jeden neu erstellten Prozess sind. freopen() sollte die gleichen fds behalten, weise ihnen einfach neue Streams zu.
Ein guter Weg, um sicherzustellen, dass dies tatsächlich das tut, was Sie wollen, wäre:
printf("Stdout is descriptor %d\n", fileno(stdout));
freopen("/tmp/newstdout", "w", stdout);
printf("Stdout is now /tmp/newstdout and hopefully still fd %d\n",
fileno(stdout));
freopen("/dev/stdout", "w", stdout);
printf("Now we put it back, hopefully its still fd %d\n",
fileno(stdout));
Ich glaube, dass dies das erwartete Verhalten von freopen() ist, wie Sie sehen können, verwenden Sie immer noch nur drei Dateideskriptoren (und zugehörige Streams).
Dies würde jede Shell-Umleitung außer Kraft setzen, da es für die Shell nichts zum Umleiten gäbe. Allerdings wird es wahrscheinlich Rohre brechen. Vielleicht möchten Sie sichergehen, dass Sie einen Handler für SIGPIPE einrichten, falls sich Ihr Programm am blockierenden Ende einer Pipe (nicht FIFO, Pipe) befindet.
Daher sollte ./your_program –stdout /tmp/stdout.txt –stderr /tmp/stderr.txt einfach mit freopen() und unter Beibehaltung der gleichen tatsächlichen Dateideskriptoren ausgeführt werden. Was ich nicht verstehe, ist, warum Sie sie zurücksetzen müssen, nachdem Sie sie geändert haben? Wenn jemand eine der beiden Optionen bestanden hat, möchte er sicherlich, dass sie bestehen bleibt, bis das Programm beendet wird?
gahooa
Die os-Funktion dup2() sollte bieten, was Sie brauchen (wenn nicht Verweise auf genau das, was Sie brauchen).
Genauer gesagt, Sie können den stdin-Dateideskriptor mit dup2() in einen anderen Dateideskriptor kopieren, andere Dinge mit stdin tun und ihn dann zurückkopieren, wenn Sie möchten.
Die Funktion dup() dupliziert einen offenen Dateideskriptor. Insbesondere stellt sie eine alternative Schnittstelle zu dem Dienst bereit, der von der Funktion fcntl() bereitgestellt wird, indem sie den konstanten Befehlswert F_DUPFD mit 0 als drittem Argument verwendet. Der duplizierte Dateideskriptor teilt alle Sperren mit dem Original.
Bei Erfolg gibt dup() einen neuen Dateideskriptor zurück, der Folgendes mit dem Original gemeinsam hat:
Gleiche geöffnete Datei (oder Pipe)
Gleicher Dateizeiger (beide Dateideskriptoren teilen sich einen Dateizeiger)
Gleicher Zugriffsmodus (Lesen, Schreiben oder Lesen/Schreiben)
Die Frage besagt jedoch ausdrücklich, dass keine systemabhängigen Funktionen verwendet werden sollen.
– abschalten
25. Februar 2009 um 6:55 Uhr
Und die Frage besagt ausdrücklich, dass die Anzahl der offenen Streams / Dateideskriptoren begrenzt wird
– Tim Post
25. Februar 2009 um 15:23 Uhr
Norman Ramsey
freopen löst den einfachen Teil. Es ist nicht schwer, die alte Standardeinstellung beizubehalten, wenn Sie nichts gelesen haben und bereit sind, POSIX-Systemaufrufe wie zu verwenden dup oder dup2. Wenn Sie anfangen, daraus zu lesen, sind alle Wetten abgeschlossen.
Vielleicht können Sie uns sagen, in welchem Kontext dieses Problem auftritt?
Ich möchte Sie ermutigen, sich an Situationen zu halten, in denen Sie bereit sind, Altes aufzugeben stdin und stdout und somit verwenden können freopen.
Die Frage besagt jedoch ausdrücklich, dass keine systemabhängigen Funktionen verwendet werden sollen.
– abschalten
25. Februar 2009 um 6:55 Uhr
Und die Frage besagt ausdrücklich, dass die Anzahl der offenen Streams / Dateideskriptoren begrenzt wird
– Tim Post
25. Februar 2009 um 15:23 Uhr
John
Und in der Zwischenzeit gibt es eine C-Quellcodebibliothek, die all dies für Sie erledigt und stdout oder stderr umleitet. Aber der coole Teil ist, dass Sie den abgefangenen Streams beliebig viele Callback-Funktionen zuweisen können, sodass Sie dann ganz einfach eine einzelne Nachricht an mehrere Ziele, eine DB, eine Textdatei usw. senden können.
Darüber hinaus ist es trivial, neue Streams zu erstellen, die genauso aussehen und sich genauso verhalten wie stdout und stderr, wobei Sie diese neuen Streams auch an mehrere Speicherorte umleiten können.
Suchen Sie auf *oogle nach der U-Streams C-Bibliothek.
14176300cookie-checkUmleitung von stdin und stdout von Cyes
Beachten Sie, dass
stdin
,stdout
undstderr
sind globale Variablen.– Alexis Wilke
22. Januar 2018 um 6:59 Uhr