9
votes

Discrimination entre les flux de fichiers et de console

Comment déterminer la météo Onetream est un fichier ou un flux de console. Dans le programme suivant, je veux imprimer "Bonjour Fichier!" Tout en écrivant dans un fichier et "Bello Console!" en écrivant à la console. Dans quelle condition dois-je spécifier à la ligne 17?

#include <fstream>
#include<iostream>
#include <string>
using namespace std;

class A{
public:
        A(string msg):_str(msg){}
        string str()const {return _str;};
private:
        string _str;
};

ostream & operator << (ostream & os, const A & a)
{
        if (os is ofstream) //this is line 17
                os << "Hello file! " << a.str() << endl;
        else
                os << "Hello console! " << a.str() << endl;

        return os;
}

int main()
{
        A a("message");
        ofstream ofile("test.txt");
        if (!ofile)
                cerr << "Unable to open file";
        else
                ofile << a;  // "Hello file"

        cout << a << endl; // "Hello console"
}


6 commentaires

La réponse est certainement dépendante du système d'exploitation. Dans les systèmes UNIX et UNIX, vous pouvez, par exemple, utiliser isatty (2) (où 2 est la FD correspondant à starr ) Pour détecter si starr pointe vers un terminal. Je n'ai aucune idée de ce que l'équivalent Windows serait.


Comme Joe Z a déclaré, il est dépendant du système d'exploitation, Windows est un peu plus dur à cause de l'API accablant IST. Check Ce pour un début.


@Joez: Même si starr pointe sur un terminal, cela ne signifie pas que l'objet de flux dans opérateur <<< / code> points à la console. Il pourrait également être fichier: vous pouvez également ouvrir n'importe quel flux dans un programme terminal!


@Nawaz: convenu. Vous devez également obtenir le fd associé au destream . Si vous redistribuez le problème comme «Comment distinguer COUT / CERR à partir d'un autre destream S?", Le problème est beaucoup plus simple et moins OS dépendante et peut-être suffisant à cet effet.


@Joez: Cela pourrait ne pas être suffisant car généralement, vous voudrez peut-être détecter une redirection de bash (pour éviter de mettre en place des caractères de contrôle des couleurs dans un fichier, par exemple). Donc, vous devez réellement avoir besoin à la fois de détecter si les points ostream sur cout ou cerr et si stdout ou < Code> stardr est un fichier TTY ou un fichier. Et bien sûr, pour ajouter du plaisir, s'il s'agit d'un TTY, vous voudrez peut-être vérifier ses propriétés pour savoir s'il soutient réellement les couleurs ...


@Matthieum.: Je veux dire "suffisant aux fins de la personne qui demande." Le problème général de la détection de la console vs. Le fichier est très spécifique. Mais, le problème réel que la personne demandant est d'essayer de résoudre peut ne pas être ce général. Nous ne saurons pas à moins qu'ils ne nous disent pas. Il peut être suffisant pour leurs besoins, il suffit de détecter si le flux est cout / cerr ou ils voudront peut-être vraiment savoir s'ils sont dirigés vers un TTY.


6 Réponses :


4
votes

Peut-être pas jolie, mais xxx

Nous pourrions utiliser & os == & std :: cout , mais la sortie standard peut être redirigée vers le fichier, donc je pense que je pense que Il est préférable d'utiliser l'objet Streambuf à la place. (Voir Cette réponse pour une meilleure compréhension quant à la redirection fonctionne et pourquoi la comparaison de Streambuf résout le problème en toute sécurité!)


19 commentaires

Et aussi, devez-vous inclure std :: cerr dans l'essai en plus de std :: cout ? c'est à dire. (& OS == & STD :: COUT || & OS == & STD :: CERR)


Je ne vois aucun problème avec cette approche. Par conséquent, +1.


@Nawaz "Je ne vois aucun problème avec cette approche": sauf, bien sûr que cela ne fonctionne pas. Si la norme OUT a été redirigée vers un fichier, par exemple, il produira toujours "bonjour console!" .


@Jameskanze: hehe. Comme Ce ? J'espère que la puissance ne fait pas ça. De plus, cela peut être corrigé: stocker la mémoire tampon de stdout dans une variable globale cachée et comparer cela à la place!


@Jameskanze: J'ai réparé ça. Voir maintenant. :-)


Il y a aussi sabog et tous les w ... variantes. Bien qu'ils utilisent probablement les mêmes tampons de flux en dessous.


Ça ne marche toujours pas. MyProg> xxx.txt devrait produire `" Bonjour fichier! ", par exemple. (C'est ce que je voulais dire par la redirection, et c'est très très commun.)


@Tobiasbrandt Ils ne peuvent pas utiliser le même streambuf, car dans un cas, le type est std :: streambuf , et dans l'autre std :: wstreambuf .


@Jameskanze: C'est une chose différente. MyProg> xxx.txt n'est pas contrôlé par MyProg plus, pas au moins par opérateur <<< / code>.


@Nawaz non, mais c'est tout le point. Sinon, vous pouvez simplement utiliser un drapeau global ou passer un argument supplémentaire. Et c'est très très courant de rediriger la sortie de cette façon; bien plus commun que des choses comme std :: Cout.rdbuf (& AfileBuf); .


@Tobiasbrandt oui et non. Un std :: ostream ne peut pas pointer sur l'un d'entre eux, de sorte que vous n'avez pas à vous soucier d'eux. Le vrai problème est que std :: cout et std :: cerr peut sortir aux fichiers, en cas de redirection et que std :: destream peut sortir à la fenêtre du terminal, par exemple Si le nom du fichier était "/ dev / tty" (sous UNIX). Le test ne fonctionne tout simplement pas.


@Jameskanze: Tout le point est dans le titre de la question qui dit "discrimination entre fichier et console flux " et cette solution le fait. :-)


@Nawaz: S'il s'agit de la question du verbatim, ce n'est peut-être pas ce que l'OP s'attend à. La détection du fichier TTY VS n'a pas de sens en soi, en général, il est utilisé pour savoir s'il faut mettre en place des caractères de formatage dans le flux (gras / normal, couleurs, ...) qui ont une signification dans TTY (OS dépendante) mais encombrer un fichier.


@Nawaz et un std :: destream ouvert avec "/ dev / tty" est un flux de console, un std :: istream initialisé avec un STD :: FILEBUF est un flux de fichiers (ou peut-être ---, il peut également s'agir d'un flux de tuyaux nommé). Et std :: cout peut être un flux de console, un flux de fichiers, un flux de tuyauterie ou beaucoup d'autres choses. Alors, quel est votre point.


Je pense que l'orchestre de la question initiale doit peser sur la manière dont la solution est généralisée. L'approche ci-dessus semble raisonnable si elles veulent simplement couvrir des cas simples, communs et ne se soucient pas vraiment de la redirection. S'ils ont besoin de quelque chose de robuste, vous devez supprimer l'API pour tout système d'exploitation que vous utilisez et écrivez un code spécifique au système d'exploitation.


@Joez "et ne vous souciez pas vraiment de la redirection": Pouvez-vous imaginer un scénario où ce serait le cas? (Au moins sous Windows et Unix. Sur un Mainframe IBM, vous pouvez généralement supposer que tout est un fichier et être fait avec elle.)


@Jameskanze: Dans mon expérience, la redirection ne semble pas vraiment tout ce qui est populaire sous Windows. Comme je l'ai dit, nous faisons preuve d'hypothèses sur le cas d'utilisation. Nous avons besoin de plus d'informations.


@Joez console Windows ne semble pas que populaire en général sous Windows. La plupart des "applications de console" sont réellement invoquées dans un fichier .bat , où la redirection est fréquente. (Bien sûr, là sont les utilisateurs de Windows comme moi-même, vous avez installé Cygwin et utilisez Bash. Et nous redirigons comme un fou.)


@Joez pas que la fréquence compte. Un programme non interactif qui écrit à std :: cout est incorrect s'il ne peut pas sortir correctement dans un fichier. (Mais bien sûr, il change la sortie lorsqu'il est dans un fichier, il est donc difficile de dire ce qu'il fait.)



4
votes

Vous pouvez (ab) utiliser tellp () code>, qui retourne -1 code> si le flux n'a pas de position:

bool isConsoleStream(ostream const& stream)
{
    return stream.tellp() == -1;
}


7 commentaires

Je pense que @kokan approche est précise . Et cette approche n'est pas, car cela dépend de la mise en œuvre!


Ce ne est pas. Tell est nécessaire pour renvoyer -1 si le flux ne prend pas en charge le positionnement. Les flux de la console ne peuvent pas prendre en charge le positionnement et les flux de fichiers.


Il peut également y avoir d'autres flux, sans soutirez le positionnement. Ce chèque ne sera donc pas en mesure de distinguer entre std :: cout et d'autres flux de ce type.


@Tobiasbrandt et les tuyaux ne supportent pas le positionnement, mais ne sont pas non plus terminaux. Il n'y a pas de réponse précise sans obtenir le niveau système FD (et peut-être même pas alors pour certains systèmes). (Mais même s'il n'est pas précis, c'est probablement assez fiable. Sauf si le code émet une sortie via un flux de filtrage qui ne prend pas en charge la recherche. Comme je fais une grande partie du temps.)


Oui, vous êtes tous les deux corrects. Mais pour distinguer des fichiers de la console, cela fonctionnera.


@Tobiasbrandt Eh bien, c'est certainement meilleur que toutes les autres solutions "portables". Et sans accès au niveau système FD, je ne pense pas qu'il existe une solution portable. Je suis habitué à programmer au niveau du système, donc j'utiliserais le ISATTY / _ISATTY Solution que j'ai proposé dans ma réponse, mais c'est tout aussi bon: il va échouer parfois, mais le mien, tout comme dans différents cas.


Bien sûr, le vrai problème est quelque chose comme myProg | OutilsProg . Que devrait-il sortir ici (puisque si la sortie se termine dans la fenêtre du terminal, dans un fichier ou est perdue quelque part dans quelque autre type est inconnu). Ma solution utilisant isatty ne dirait pas un terminal, votre solution dirait que non un fichier; Si votre fonction était précise, elle retournerait peut-être , mais c ++ ne prend pas en charge cela :-). (Bien que le retour d'un faillible serait une bonne idée, si il y avait quelques moyens d'obtenir les informations.)



2
votes

one est un destream code> et l'autre est un ostream code>. Il suffit de disposer de deux méthodes.

#include <iostream>
#include <string>
#include <fstream>

class A {
    std::string s;
public:
    A(const std::string& s) : s(s){}
    std::string str() const {return s;}
};


ostream & operator << (std::ostream & os, const A & a)
{
    return os << "console: " << a.str() << std::endl;
}

ofstream & operator << (std::ofstream & os, const A & a)
{
    return os << "file: " << a.str() << std::endl;
}

int main()
{
    A a("hello world");
    std::cout << a << endl;
}


7 commentaires

Je ne peux pas tester la version du fichier pour ce guichet automatique.


Ceci est (ou peut-être) simplement faux. Un ostream peut pointer vers un fichier, et un fichier ofstream peut pointer sur un périphérique interactif (et bien sûr, std :: cout pourrait être un std :: destream ).


ostream os = fbrieam ("un fichier"); est << a << endl; va écrire "console: ..." dans un fichier.


Je sais cout est extern ostream cout mais je ne savais jamais que cela pourrait être un std :: destream . Mais la conversion ostream OS = ofstream ("Certains fichier"); est très probable. C'est jusqu'à OP pour déterminer si cette solution correspondrait à ses besoins actuels et futurs.


@TOBIASBRANDT OSTREAM OS = OFStream ("Certains fichier"); ne doit pas compiler. std :: filebuf fb ("Quelque fichier", std :: ios_base :: sortir); STD :: Ostream OS (& FB); sera cependant un exemple parfait du problème que vous essayez d'augmenter. C'est aussi un idiome très très fréquent, car il permet la sortie d'un fichier, si le nom de fichier est donné et la sortie de std :: cout si ce n'est pas le cas.


@andre bien, la norme fait dit qu'ils ont le type std :: ostream , mais ceci est probablement un exemple de spécification; Historiquement, sur la plupart des implémentations, ils avaient un type de type dérivé de std :: ostream .


Et bien sûr, en utilisant deux fonctions échoue, seuls si l'utilisateur écrit quelque chose comme somestream << "message:" << A; .



0
votes

Ceci fonctionne sur Visual Studio 2012

if (typeid(os) == typeid(ofstream)) //this is line 17


2 commentaires

Cela ne fonctionne nulle part, car il ne prend pas en compte la redirection, ni ostream qui sont initialisés avec un FileBuf .


Dans ma défense, il travaille quelque part, c'est-à-dire que dans le code effectivement soumis par le questionneur. Mais je conviens que d'autres réponses sont meilleures.



3
votes

Il n'y a pas de moyen portable. Sous UNIX, vous pouvez faire: xxx

sous Windows, le isatty devient _isatty , et je ne suis pas sûr que les macros existent (mais je soupçonne qu'ils le font).

Bien sûr, cela suppose que vous ne faites pas les choses à confondes dans votre code. Quelque chose comme: xxx

par exemple, ou: xxx

ou même: xxx < / Pré>

Mais il est à peu près aussi proche que possible de l'obtenir sans le FD à partir du filebuf .


0 commentaires

0
votes

Fonction Pour vérifier si un flux de caractères C ++ est connecté à une borne / console / tty.

Idéalement, nous utiliserions le descripteur de fichier sous la forme du tampon de flux du flux de STDIO C ++ (CIN, COUT, CERR ou sabot). Cependant, il n'ya aucun moyen de récupérer le descripteur de fichier sous-porté. Nous utilisons donc le fait que lors du démarrage du programme Les tampons de flux STDIO sont connectés à l'entrée et à la sortie standard du programme.

Cette fonction ne fonctionne que dans les conditions suivantes:

  1. Les tampons de flux des flux de start-up c ++ stdio ne doivent pas changer. Étant donné que les adresses des tampons de flux des flux STDIO start-up C ++ sont utilisés comme identifiants. Par exemple, en les supprimant, puis affectant un nouveau tampon de flux qui a la même adresse que l'un de ces tampons de flux des flux STDIO STDIO STRAINT-UP C ++.

  2. Le STDIO du programme ne doit pas changer après le démarrage du programme. Parce que les statuts TTY des tampons de flux STDIO sont stockés au démarrage du programme. Par exemple, si au démarrage du STD. Out est connecté à un terminal et plus tard, il est redirigé vers un tuyau ou un fichier par quelque chose d'extérieur au programme. [Au lieu de stocker les statuts TTY au démarrage, vous pouvez les récupérer au moment de l'exécution, mais vous devez alors vous assurer que votre programme (et toutes les bibliothèques utilisées) ne modifie pas les descripteurs de fichiers STDIO (0, 1 et 2) . Ramber que les tampons de flux STDIO utilisent probablement les autres descripteurs de fichiers (duplicataires).]

    code: xxx

    Remarque: Je ne l'ai testé que sous Linux.


0 commentaires