2016-01-18 2 views
1

Следующий простой скрипт (в основном) slurps на своем входе, разделяет его в соответствии с регулярным выражением, заменяет все строки новой строки в каждом элементе результирующего списка и печатает из модифицированных элементов один за другим:Ошибка Unicode только при запуске кода с флагом -n в командной строке

# demo.pl 
use strict; 
use utf8; 
use open qw(:std :utf8); 
use warnings qw(FATAL utf8); 

BEGIN { $/ = $\ = undef; } 

while (<>) { 
    s/\n\z//; 
    s/\n/\\n/g, print "$_\n" for split /\n(?=[^\W\d]\w*=)/; 
} 

Когда дан входной файл (INPUTFILE) со следующим (UTF-8 кодировкой) содержанием в качестве аргумента

A=42 
ΦΡΩΒΩΖΖ=ABCDEFGHIJKLMNOPQRSTUVWXYZ 
_B_C_D12= 
foo 
345bar=nope 
baz 
    =whatever= 
X_Y_Z=quux 

... он печатает желаемый выход, а именно:

% perl demo.pl INPUTFILE 
A=42 
ΦΡΩΒΩΖΖ=ABCDEFGHIJKLMNOPQRSTUVWXYZ 
_B_C_D12=\nfoo\n345bar=nope\nbaz\n =whatever= 
X_Y_Z=quux 

В отличие от этого, следующие практически идентичны CLI однострочник

% perl -ne 'use strict; use utf8; use open qw(:std :utf8); use warnings qw(FATAL utf8); BEGIN { $/ = $\ = undef; } s/\n\z//; s/\n/\\n/g, print "$_\n" for split /\n(?=[^\W\d]\w*=)/;' INPUTFILE 

... производит следующие действия для того же входного файла

A=42\nΦΡΩÎΩÎÎ=ABCDEFGHIJKLMNOPQRSTUVWXYZ 
_B_C_D12=\nfoo\n345bar=nope\nbaz\n =whatever= 
X_Y_Z=quux 

Там являются (видимо) две проблемы здесь :

  1. регулярное выражение не может отделить первый и второй элементы;
  2. вывод содержит неразборчивую подстроку.

(я надеюсь, что обе эти проблемы будут иметь те же основные причины.)


Единственное различие между «в-файл сценария» (demo.pl) и CLI однострочника является то, что бывший явно обертывает тело скрипта while (<>) { ... }, тогда как для последнего флаг -n заставляет эту оболочку вставляться автоматически.

Q: Как следует один вкладыш выше быть модифицирована таким образом, что она производит желаемый результат с -n флагом?


Кстати, не удивительно, что точной командной строки эквивалент demo.pl (без -n флага), а именно

% perl -e 'use strict; use utf8; use open qw(:std :utf8); use warnings qw(FATAL utf8); BEGIN { $/ = $\ = undef; } while (<>) { s/\n\z//; s/\n/\\n/g, print "$_\n" for split /\n(?=[^\W\d]\w*=)/; }' INPUTFILE 

также производит желаемый результат.

Таким образом, проблема, независимо от того, что это такое, имеет какое-то отношение к флагом -n.


FWIW:

% perl -v | head -2 

This is perl 5, version 20, subversion 2 (v5.20.2) built for x86_64-linux-gnu-thread-multi 

EDIT: Еще один ключ: если вход неисправного однострочника пропускают через STDIN, а не в качестве имени файла в @ARGV (например,заменить INPUTFILE с < INPUTFILE), то он производит желаемый результат полностью разборчиво, хотя все еще некорректной, выход:

A=42\nΦΡΩΒΩΖΖ=ABCDEFGHIJKLMNOPQRSTUVWXYZ 
_B_C_D12=\nfoo\n345bar=nope\nbaz\n =whatever= 
X_Y_Z=quux 

Мой текущий догадаться, что use open qw(:std :utf8) не покрывает входной поток, который <> считывает с момента, когда вход передается как имя файла в @ARGV.

ответ

3

Единственное различие между «в-файлом сценарием» (demo.pl) и CLI однострочником является то, что бывшая явно оборачивает тело сценария с временем (<>) {...} , тогда как для последнего флаг -n заставляет эту оболочку вставляться автоматически.

Да, точно - -n обручи все код в while (<>) { ... }. Это включает в себя ваши строки use utf8; и use open(:utf8);, поэтому файл уже открыт к тому моменту, когда вы активируете Unicode.

Вы можете легко проверить, запустив эквивалентную программу до версии -n:

while (<>) { 
    # demo.pl 
    use strict; 
    use utf8; 
    use open qw(:std :utf8); 
    use warnings qw(FATAL utf8); 
    BEGIN { $/ = $\ = undef; } 
    s/\n\z//; 
    s/\n/\\n/g, print "$_\n" for split /\n(?=[^\W\d]\w*=)/; 
} 

и видим тот же эффект.


Более интересно, вы можете увидеть, что use декларации все еще есть эффект: запустить тот же входной файл через два раза

perl demo.pl INPUTFILE INPUTFILE 

и вы получите два выхода, первый один сломан, второй правильный. Это также происходит с вашим однострочным.


Вы можете включить UTF-8 для ввода по умолчанию, используя -C flag with the i (8) вариант:

perl -CiO -ne 'use strict; use utf8; BEGIN { $/ = $\ = undef; } s/\n\z//; s/\n/\\n/g, print "$_\n" for split /\n(?=[^\W\d]\w*=)/;' INPUTFILE 

Это гарантирует, что UTF-8 включен до того, как файл открыт, и вы получите правильный выход. O также позволяет использовать UTF-8 для стандартного вывода, так что вы можете распечатать его.

+0

Это сбивает с толку. Я понял, что 'use ...' подразумевает' BEGIN {...} '. Я думал, это означало, что это произойдет, когда какой-либо из остальной части кода будет обработан ... – kjo

+0

Спасибо. FWIW, версия, которую вы показываете только с помощью '-Ci' (в отличие от' -CiO'), терпит неудачу с вводом, данным в вопросе, из-за 'использования предупреждений qw (FATAL utf8)' вместе с наличием широких символов в выход. Избавление от 'использования предупреждений qw (FATAL utf8)' позволяет программе закончить, но все же предупреждение печатается. Нижняя строка: она должна быть '-CiO', а не' -Ci'. – kjo

+0

'use' немного волшебный некоторое время. Хороший вопрос об предупреждениях - теперь редактируется. –