Нижняя шкала 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.