web-dev-qa-db-fra.com

Lire à partir d'un fichier ou STDIN

J'ai écrit un utilitaire de ligne de commande qui utilise getopt pour analyser les arguments donnés sur la ligne de commande. Je voudrais également qu'un nom de fichier soit un argument facultatif, comme c'est le cas dans d'autres utilitaires comme grep, cut etc. Donc, je voudrais qu'il ait l'utilisation suivante

tool -d character -f integer [filename]

Comment puis-je implémenter les éléments suivants?

  • si un nom de fichier est donné, lisez le fichier.
  • si aucun nom de fichier n'est donné, lisez à partir de STDIN.
58
Ryan R. Rosario

En termes simples:

import sys
# parse command line
if file_name_given:
    inf = open(file_name_given)
else:
    inf = sys.stdin

À ce stade, vous utiliseriez inf pour lire le fichier. Selon qu'un nom de fichier a été donné, cela se lit à partir du fichier donné ou de stdin.

Lorsque vous devez fermer le fichier, vous pouvez le faire:

if inf is not sys.stdin:
    inf.close()

Cependant, dans la plupart des cas, il sera inoffensif de fermer sys.stdin si vous en avez terminé.

58
Greg Hewgill

Le module fileinput peut faire ce que vous voulez - en supposant que les arguments sans option sont dans args alors:

import fileinput
for line in fileinput.input(args):
    print line

Si args est vide, alors fileinput.input() lira à partir de stdin; sinon, il lit à partir de chaque fichier tour à tour, d'une manière similaire à while(<>) de Perl.

74
SimonJ

J'aime l'idiome général de l'utilisation d'un gestionnaire de contexte, mais la solution (trop) triviale finit par fermer sys.stdin Lorsque vous êtes en dehors de l'instruction with, ce que je veux éviter.

Emprunter de cette réponse , voici une solution:

import sys
import contextlib

@contextlib.contextmanager
def _smart_open(filename, mode='Ur'):
    if filename == '-':
        if mode is None or mode == '' or 'r' in mode:
            fh = sys.stdin
        else:
            fh = sys.stdout
    else:
        fh = open(filename, mode)
    try:
        yield fh
    finally:
        if filename is not '-':
            fh.close()

if __== '__main__':
    args = sys.argv[1:]
    if args == []:
        args = ['-']
    for filearg in args:
        with _smart_open(filearg) as handle:
            do_stuff(handle)

Je suppose que vous pourriez obtenir quelque chose de similaire avec os.dup() mais le code que j'ai préparé pour le faire s'est avéré être plus complexe et plus magique, alors que ce qui précède est quelque peu maladroit mais très simple .

16
tripleee

Pour utiliser l'instruction with de python, on peut utiliser le code suivant:

import sys
with open(sys.argv[1], 'r') if len(sys.argv) > 1 else sys.stdin as f:
    # read data using f
    # ......
11
Roun

Je préfère utiliser "-" comme indicateur que vous devriez lire depuis stdin, c'est plus explicite:

import sys
with open(sys.argv[1], 'r') if sys.argv[1] is not "-" else sys.stdin as f:
    pass # do something here
9
Phil L.

Pas une réponse directe mais liée.

Normalement, lorsque vous écrivez un script python, vous pouvez utiliser le package argparse. Si tel est le cas, vous pouvez utiliser:

parser = argparse.ArgumentParser()
parser.add_argument('infile', nargs='?', type=argparse.FileType('r'), default=sys.stdin)

'?'. Un argument sera consommé à partir de la ligne de commande si possible, et produit comme un seul élément. Si aucun argument de ligne de commande n'est présent, la valeur par défaut sera produite.

et ici nous définissons la valeur par défaut sur sys.stdin;

donc s'il y a un fichier il le lira, et sinon il prendra l'entrée de stdin "Remarque: que nous utilisons l'argument positionnel dans l'exemple ci-dessus"

pour plus de visite: https://docs.python.org/2/library/argparse.html#nargs

5
Medhat

Quelque chose comme:

if input_from_file:
    f = open(file_name, "rt")
else:
    f = sys.stdin
inL = f.readline()
while inL:
    print inL.rstrip()
    inL = f.readline()
1
GreenMatt