Я пишу скрипт для добавления текстового файла, добавляя некоторый текст в определенную строку в файле после интервала табуляции. Требуется помощь, чтобы добавить новую строку и интервал табуляции после совпадающей строки «яблоко» в приведенном ниже случае.
Пример файла:
apple
<tab_spacing>original text1
orange
<tab_spacing>original text2
Ожидаемый результат:
apple
<tab_spacing>testing
<tab_spacing>original text1
orange
<tab_spacing>original text2
Что я пробовал:
use strict;
use warnings;
my $config = "filename.txt";
open (CONFIG,"+<$config") or die "Fail to open config file $config\n";
while (<CONFIG>) {
chop;
if (($_ =~ /^$apple$/)){
print CONFIG "\n";
print CONFIG "testing\n";
}
}
close CONFIG;
Насколько велик файл?
@zdim просто текстовый файл, будет около нескольких килобайт.
Не совсем понятно, что вы подразумеваете под «интервалом табуляции», но, возможно, вы ищете:
perl -pE 'm/^(\t*)/; say "${1}testing" if $a; $a = /apple/' filename.txt
Я подозреваю, что вы на самом деле хотите \s
вместо \t
, но YMMV. По сути, в каждой строке ввода вы сопоставляете начальный пробел, а затем печатаете строку с этим пробелом и строкой «тестирование», если предыдущая строка совпала.
Чтобы написать это подробно:
#!/usr/bin/env perl
use 5.12.0;
use strict;
use warnings;
my $n = 'filename.txt';
open my $f, '<', $n, or die "$n: $!\n";
while(<$f>){
m/^(\t*)/; # possibly \s is preferred over \t
say "${1}testing" if $a;
$a = /apple/;
print;
}
Привет, интервал табуляции означает использование клавиши табуляции на клавиатуре для создания интервала. Вы можете попробовать ввести клавишу табуляции в любом файле, и вы ее получите.
Привет, ваш код выше удаляет исходный текст под яблоком? Я хотел бы сохранить исходный текст и добавить новую строку текста под яблоком с табуляцией
@peace, ты выполнил код? Он сохраняет весь исходный контент и просто вставляет одну строку после каждой строки, которая соответствует apple
. Вставляемая строка имеет те же начальные табуляции (или пробелы, в зависимости от того, используете ли вы \t
или \s
), что и следующая строка. Если последняя строка файла соответствует слову «яблоко», дополнительная строка не добавляется.
Мы не можем просто «добавить» текст в середину файла, как пытались. Файл представляет собой последовательность байтов, и их нельзя добавлять или удалять (кроме как в конце), а только изменять. Поэтому, если мы начнем запись в середину файла, мы изменим там байты, поэтому перезапишем то, что следует за этим местом. Вместо этого нам приходится копировать остальной текст и записывать его обратно после «дополнения» или копировать файл, добавляя текст в процессе.
Еще один способ - прочитать весь файл в строку и запустить для него регулярное выражение, чтобы изменить его, а затем записать новую строку. Предполагая, что файл не слишком велик для этого
perl -0777 -pe's{apple\n\K(\t)}{Added text\n$1}g' in.txt
Переключатель -0777
заставляет его читать весь файл в строку («глотать»), доступную в $_
, к которой по умолчанию привязано регулярное выражение. Этот \K
, который является просмотром назад , отбрасывает предыдущие совпадения, поэтому они не поглощаются из строки, и нам не нужно (захватывать и) возвращать их обратно. С модификатором /g
он продолжает перебирать всю строку, чтобы найти и изменить все вхождения шаблона.
Это выводит измененный файл на экран, что можно сохранить в новом файле, перенаправив его
perl -0777 -pe'...' in.txt > out.txt
Или можно изменить входной файл «на месте» с помощью -i
perl -0777 -i.bak -pe'...' in.txt
.bak
позволяет сохранить оригинал с расширением .bak
. См. переключатели в perlrun.
Другой способ — использовать предпросмотр для того, что следует далее (вкладка), чтобы нам не приходилось захватывать и возвращать его обратно.
perl -0777 -pe's{apple\n\K(?=\t)}{Added text\n}g' in.txt
Все это приводит к желаемому изменению.
Обратите внимание на эту вкладку ("tab_spacing")
Приведенное выше регулярное выражение предполагает символ табуляции в начале строки, следующей за строкой с apple
. Когда мы говорим «табуляция», мы имеем в виду один символ (табуляцию).
Но есть много причин, по которым на самом деле может не быть символа табуляции, даже если он выглядит так, как будто он есть. Пример: все табуляции могут быть автоматически заменены пробелами редактором.
Поэтому может быть безопаснее использовать \s+
(несколько пробелов) вместо \t
в регулярном выражении.
s{apple\n\K(\s+)}{Added text\n$1}g
или
s{apple\n\K(?=\s+)}{Added text\n}g
Если это должно быть сделано внутри существующей более крупной программы Perl (а не как программа командной строки, «однострочная», как указано выше), один из способов
use Path::Tiny; # path(), constructor
my $file_content = path($file)->slurp; # read the file into a string
# Now use a regex; all discussion above applies
$file_content =~ s{apple\n\K(?=\t)}{Added text\n}g;
# Print out $file_content, to be redirected etc. Or write to a file
path($new_file)->spew($file_content);
Я использую библиотеку Path::Tiny, чтобы "слепить" файл в строку и spew
записать $file_content
в новый файл. Это необходимо установить, так как оно не находится в «ядре» (обычно не устанавливается вместе с Perl), и если это проблема по какой-то странной причине, вот своего рода идиома для него без каких-либо библиотек
my $file_content = do {
local $/;
open my $fh, '<', $file or die "Can't open $file: $!";
<$fh>;
};
или даже
my $file_content = do { local (@ARGV, $/) = $file; <> };
(см. этот пост для некоторых объяснений и ссылок)
Привет, это отличная информация. Я это попробую.
@peace Это довольно стандартный способ внесения изменений в файл - прочитать весь файл в строку, добавить к нему регулярное выражение, распечатать его. (Или измените входной файл напрямую с помощью -i
). Наличие регулярного выражения для строки со всем файлом упрощает работу с шаблонами, которые (могут) охватывать несколько строк. Одно ограничение, если файл гигантский. Но kBs ничего
В новых версиях Perl есть -g
вместо -0777
. Говорят это для "жрать" или "глотать" (или "хватать")...
Привет @zdim, я попробовал ваше предложение perl -0777 -pe's{apple\n\K(?=\t)}{Added text\n}' in.txt
оно работает при прямом выполнении в оболочке, но не в сценарии .pl: system("perl -0777 -pe's{apple\n\K(?=\s+)}{testing\n}' in.txt")
Есть идеи?
@peace Итак, это perl ...
, показанное здесь, предназначено для работы в оболочке. Это так называемая «однострочная программа» на Perl (между ''
), которую можно выполнить прямо в командной строке оболочки. Но если у вас есть программа на Perl (в файле), то вы можете выполнить работу непосредственно в ней, а не обращаться к оболочке (что и делает system
) для вызова другой программы на Perl (этой однострочной). В программе на Perl непосредственно прочитайте файл, измените его и распечатайте обратно. Я добавлю код для этого в ответ, когда поймаю момент.
@peace В конец добавлен способ сделать это в существующей (предположительно большей) программе Perl.
Честно говоря, некоторые довольно странные вещи в вашем коде:
CONFIG
) вместо лексической переменной и двух аргументов open()
вместо версии с тремя аргументами (open my $config_fh, '+<', $config'
) заставляет меня думать, что вы используете довольно старые учебники по Perl.chop()
вместо chomp()
заставляет меня думать, что вы используете какие-то древние учебники по Perl$
в вашем регулярном выражении - ^$apple$
, вероятно, должно быть ^apple$
Кроме того, Tie::File уже более двадцати лет входит в стандартную библиотеку Perl и значительно облегчит эту задачу.
#!/usr/bin/perl
use strict;
use warnings;
use Tie::File;
tie my @file, 'Tie::File', 'filename.txt' or die $!;
for (0 .. $#file) {
if ($file[$_] eq 'apple') {
splice @file, $_ + 1, 0, "\ttesting\n";
}
}
Вы должны включить
$!
в сообщение об ошибке.