Нижняя шкала script в Python против Perl

В Perl для ввода текстового файла в нижний регистр я мог бы сделать следующее lowercase.perl:

#!/usr/bin/env perl

use warnings;
use strict;

binmode(STDIN, ":utf8");
binmode(STDOUT, ":utf8");

while(<STDIN>) {
  print lc($_);
}

И в командной строке: perl lowercase.perl < infile.txt > lowered.txt

В Python я мог бы сделать с lowercase.py:

#!/usr/bin/env python
import io
import sys

with io.open(sys.argv[1], 'r', 'utf8') as fin:
    with io.open(sys.argv[2], 'r', 'utf8') as fout:
        fout.write(fin.read().lower())

И в командной строке: python lowercase.py infile.txt lowered.txt

Является ли Perl lowercase.perl отличным от Python lowercase.py?

Он передает поток и строит его по мере его вывода? Или он читает весь файл, как Python lowercase.py?

Вместо того, чтобы читать в целом файле, есть способ передать поток в Python и вывести байт с опущенным байтом или char на char?

Есть ли способ управлять синтаксисом командной строки, чтобы он соответствовал Perl STDIN и STDOUT? Например. python lowercase.py < infile.txt > lowered.txt?

Ответ 1

Здесь, кажется, есть две проблемы с чередованием, и я сначала обращаюсь к этому. Для того, чтобы заставить Perl и Python использовать вызов с очень похожим поведением, см. Вторую часть сообщения.

Короткие:. Они различаются тем, как они работают с I/O, но оба работают поочередно, а код Python легко изменен, чтобы использовать тот же вызов командной строки, что и код Perl. Кроме того, обе записи могут быть записаны так, чтобы разрешить ввод либо из файла, либо из стандартного потока ввода.


(1) Оба ваших решения являются "потоковыми", в том смысле, что они оба обрабатывают ввод последовательно. Код Perl читается с STDIN, тогда как код Python получает данные из файла, но оба они получают строку за раз. В этом смысле они сопоставимы по эффективности для больших файлов.

Стандартный способ чтения и записи файлов по очереди в Python -

with open('infile', 'r') as fin, open('outfile', 'w') as fout:
    fout.write(fin.read().lower())

См., например, эти сообщения SO на обработке очень большого файла и файлы для чтения и записи. То, как вы читаете файл, кажется идиоматичным для линейной обработки, см., Например, сообщения SO в чтении больших файлов по очереди, на идиоматическое линейное чтение, а другое - по очереди.

Измените первый, открытый здесь, на io.open, чтобы напрямую взять первый аргумент из командной строки в качестве имени файла и при необходимости добавить режимы.

(2) Командная строка с перенаправлением ввода и вывода, которую вы показываете, является функцией оболочки

./program < input > output

program подается через стандартный входной поток (дескриптор файла 0). Они предоставляются из файла input оболочкой через перенаправление <. Из gnu bash руководство (см. 3.6.1), где слово "слово" означает наш "enter"

Перенаправление ввода приводит к тому, что файл, имя которого возникает из расширения слова, которое нужно открыть для чтения в дескрипторе файла n, или стандартный ввод (дескриптор файла 0), если n не указано.

Любая программа может быть записана для этого, т.е. действовать как фильтр. для Python вы можете использовать

import sys   
for line in sys.stdin:
    print line.lower()

См., например, сообщение о фильтрах писем. Теперь вы можете вызвать его как script.py < input в оболочке.

Код print для стандартного вывода, который затем может быть перенаправлен оболочкой с помощью >. Затем вы получаете тот же вызов, что и для Perl script.

Я полагаю, что стандартное перенаправление вывода > ясно в обоих случаях.


Наконец, вы можете привести оба к почти идентичному поведению и разрешить либо вызов, таким образом.

В Perl существует следующая идиома

while (my $line = <>) {
    # process $line
}

Оператор алмаза <> либо берет строку за строкой из всех файлов, представленных в командной строке (которые находятся в @ARGV), либо получает свои строки из STDIN (если данные каким-то образом передаются в script). Из Операторы ввода/вывода в perlop

Нулевой дескриптор файла <> является особым: его можно использовать для эмуляции поведения sed и awk и любой другой программы фильтрации Unix, которая принимает список имен файлов, делая то же самое с каждой строкой ввода от всех них, Вход из <> поступает либо из стандартного ввода, либо из каждого файла, указанного в командной строке. Здесь, как это работает: в первый раз <> оценивается массив @ARGV, а если он пуст, $ARGV[0] устанавливается в "-", который при открытии дает вам стандартный ввод. Массив @ARGV затем обрабатывается как список имен файлов.

В Python вы получаете практически такое же поведение

import fileinput
for line in fileinput.input():
    # process line

Это также проходит через строки файлов с именем sys.argv, по умолчанию sys.stdin, если список пуст. Из fileinput документация

Итерирует по всем файлам, перечисленным в sys.argv[1:], по умолчанию sys.stdin, если список пуст. Если имя файла '-', оно также заменяется на sys.stdin. Чтобы указать альтернативный список имен файлов, передайте его в качестве первого аргумента в input(). Также разрешено одно имя файла.

В обоих случаях, если есть дополнительные аргументы командной строки, отличные от имен файлов.

С этим вы можете использовать как скрипты Perl, так и Python в любом случае

lowercase < input > output
lowercase input   > output

Или, если на то пошло, как cat input | lowercase > output.


Все методы здесь читают ввод и вывод записи по очереди. Это может быть дополнительно оптимизировано (забуферировано) с помощью интерпретатора, системы и перенаправления оболочки. Можно изменить это так, чтобы читать и/или писать в небольших кусках, но это было бы крайне неэффективно и заметно замедляло бы программы.

Ответ 2

эквивалент Python 3.x для вашего кода Perl может выглядеть следующим образом:

#!/usr/bin/env python3.4
import sys

for line in sys.stdin:
    print(line[:-1].lower(), file=sys.stdout)

Он читает stdin по строкам и может использоваться в конвейере оболочки

Ответ 3

Слегка от темы (в зависимости от вашего определения "Perl" ), но, может быть, интересного...

perl6 -e  ' .lc.say for "infile.txt".IO.lines ' > lowered.txt

Это не обрабатывает "байт по байту", не "весь файл", а "строка за строкой". .lines создает ленивый список, поэтому вы не будете использовать тонну памяти, если ваш файл большой. Предполагается, что файл является текстовым (это означает, что вы читаете Str, а не Buf байтов при чтении), а по умолчанию используется кодировка "Unicode" - значение open будет пытаться выяснить, что используется UTF, и если это не может ли он использовать UTF-8. Подробнее здесь.

По умолчанию окончание строк chomp 'ed при чтении и возврате на say - если требования к обработке запрещают это, вы можете передать логический именованный параметр :chomp в .lines (и использовать .print, а не .say);

$ perl6 -e  ' .lc.print for "infile.txt".IO.lines(:!chomp) ' > lowered.txt

Вы можете избежать перенаправления ввода-вывода и сделать все это в perl6, но это будет читать весь файл в виде одного Str;

$ perl6 -e  ' "lowered.txt".IO.spurt: "infile.txt".IO.slurp.lc '

Ответ 4

Является ли Perl lowercase.perl отличным от Python lowercase.py?

Файл Python принимает имена файлов для ввода и вывода. Файл Perl выполняет потоковое вещание (например, может использоваться в some_command | your_perl_script.pl | some_other command).

Он передает поток и строит его по мере его выхода? Или он читает весь файл, как Python lowercase.py?

while(<STDIN>) {

проходит через ваш ввод строки за строкой. Пока ваш вход содержит \n (по умолчанию разрыв строки, может быть изменен на установка $/). Это потоковая передача.

Вместо того, чтобы читать в целом файле, есть ли способ передать поток в Python и вывести байт с опущенным байтом или char на char?

Возможно, да, но я не знаю Python: (

Ответ 5

В этом примере единственная разница заключается в том, как получить доступ к данным. Один из них - это открытие файла (версия python), другое - piping i/o для программы (версия perl). Любой язык может получить доступ к данным любым способом.

Примеры работы с stdin/stdout в python:

Ответ 6

Здесь я вижу два вопроса:

  • как вести строчный текст без чтения всего файла: Прочитать его по строкам
  • как обрабатывать аргументы командной строки и по умолчанию использовать stdin, если нет: Использовать fileinput.

Вот как:

Для ввода текста в нижнем регистре просто используйте fin.readline() или просто итетерируйте файловый объект (который читает по одной строке за раз):

for line in fin:
    ...

Чтобы обрабатывать имена файлов, указанные в командной строке, с stdin, если нет, используйте fileinput. Если вы просто отправите все на stdout, этого будет достаточно:

for line in fileinput.input():
    print(line.lower(), end="")

Но если вы хотите сместить большой корпус и сохранить результат на диск, вероятно, вы захотите вывести каждый файл отдельно. Это немного больше работает, поскольку fileinput не будет автоматически перенаправлять ваш вывод. Здесь один из способов:

currentname = None
for line in fileinput.input():
    if fileinput.isfirstline():
        if currentname and currentname != "<stdin>":  # clean up after previous file
            fout.close()

        currentname = fileinput.filename()        # Set up for new file
        if currentname == "<stdin>":
            fout = sys.stdout
        else:
            fout = open(currenttname+"-low", "w"
    fout.write(line.lower())

)

Я написал каждый файл <name> на <name>-low, но вы можете, конечно, заменить любой другой подход (например, использовать одно и то же имя для вывода, но в другом каталоге).

Ответ 7

Программа Python попытается прочитать весь входной файл. Вызов read() без аргумента будет читать до EOF, см. документацию модуля io.

Также есть небольшая ошибка, fout следует открыть в режиме "w".

Как упоминалось @denis-shatov, можно написать эквивалент Python script для Perl.