Perl добавить строку в текстовый файл

Я пишу скрипт для добавления текстового файла, добавляя некоторый текст в определенную строку в файле после интервала табуляции. Требуется помощь, чтобы добавить новую строку и интервал табуляции после совпадающей строки «яблоко» в приведенном ниже случае.

Пример файла:

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;

Вы должны включить $! в сообщение об ошибке.

William Pursell 16.02.2023 04:55

Насколько велик файл?

zdim 16.02.2023 08:54

@zdim просто текстовый файл, будет около нескольких килобайт.

peace 16.02.2023 13:54
Ускорьте разработку веб-приложений Laravel с помощью этих бесплатных стартовых наборов
Ускорьте разработку веб-приложений Laravel с помощью этих бесплатных стартовых наборов
Laravel - это мощный PHP-фреймворк, используемый для создания масштабируемых и надежных веб-приложений. Одним из преимуществ Laravel является его...
Что такое двойные вопросительные знаки (??) в JavaScript?
Что такое двойные вопросительные знаки (??) в JavaScript?
Как безопасно обрабатывать неопределенные и нулевые значения в коде с помощью Nullish Coalescing
Создание ресурсов API Laravel: Советы по производительности и масштабируемости
Создание ресурсов API Laravel: Советы по производительности и масштабируемости
Создание API-ресурса Laravel может быть непростой задачей. Она требует глубокого понимания возможностей Laravel и лучших практик, чтобы обеспечить...
Как сделать компонент справочного центра с помощью TailwindCSS
Как сделать компонент справочного центра с помощью TailwindCSS
Справочный центр - это веб-сайт, где клиенты могут найти ответы на свои вопросы и решения своих проблем. Созданный для решения многих распространенных...
Асинхронная передача данных с помощью sendBeacon в JavaScript
Асинхронная передача данных с помощью sendBeacon в JavaScript
В современных веб-приложениях отправка данных из JavaScript на стороне клиента на сервер является распространенной задачей. Одним из популярных...
Как подобрать выигрышные акции с помощью анализа и визуализации на Python
Как подобрать выигрышные акции с помощью анализа и визуализации на Python
Отказ от ответственности: Эта статья предназначена только для демонстрации и не должна использоваться в качестве инвестиционного совета.
1
3
72
3
Перейти к ответу Данный вопрос помечен как решенный

Ответы 3

Не совсем понятно, что вы подразумеваете под «интервалом табуляции», но, возможно, вы ищете:

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 16.02.2023 06:32

Привет, ваш код выше удаляет исходный текст под яблоком? Я хотел бы сохранить исходный текст и добавить новую строку текста под яблоком с табуляцией

peace 16.02.2023 06:43

@peace, ты выполнил код? Он сохраняет весь исходный контент и просто вставляет одну строку после каждой строки, которая соответствует apple. Вставляемая строка имеет те же начальные табуляции (или пробелы, в зависимости от того, используете ли вы \t или \s), что и следующая строка. Если последняя строка файла соответствует слову «яблоко», дополнительная строка не добавляется.

William Pursell 16.02.2023 14:32

Мы не можем просто «добавить» текст в середину файла, как пытались. Файл представляет собой последовательность байтов, и их нельзя добавлять или удалять (кроме как в конце), а только изменять. Поэтому, если мы начнем запись в середину файла, мы изменим там байты, поэтому перезапишем то, что следует за этим местом. Вместо этого нам приходится копировать остальной текст и записывать его обратно после «дополнения» или копировать файл, добавляя текст в процессе.

Еще один способ - прочитать весь файл в строку и запустить для него регулярное выражение, чтобы изменить его, а затем записать новую строку. Предполагая, что файл не слишком велик для этого

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 16.02.2023 13:59

@peace Это довольно стандартный способ внесения изменений в файл - прочитать весь файл в строку, добавить к нему регулярное выражение, распечатать его. (Или измените входной файл напрямую с помощью -i). Наличие регулярного выражения для строки со всем файлом упрощает работу с шаблонами, которые (могут) охватывать несколько строк. Одно ограничение, если файл гигантский. Но kBs ничего

zdim 16.02.2023 20:36

В новых версиях Perl есть -g вместо -0777. Говорят это для "жрать" или "глотать" (или "хватать")...

zdim 16.02.2023 20:58

Привет @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 17.02.2023 04:53

@peace Итак, это perl ..., показанное здесь, предназначено для работы в оболочке. Это так называемая «однострочная программа» на Perl (между ''), которую можно выполнить прямо в командной строке оболочки. Но если у вас есть программа на Perl (в файле), то вы можете выполнить работу непосредственно в ней, а не обращаться к оболочке (что и делает system) для вызова другой программы на Perl (этой однострочной). В программе на Perl непосредственно прочитайте файл, измените его и распечатайте обратно. Я добавлю код для этого в ответ, когда поймаю момент.

zdim 17.02.2023 08:02

@peace В конец добавлен способ сделать это в существующей (предположительно большей) программе Perl.

zdim 17.02.2023 08:17
Ответ принят как подходящий

Честно говоря, некоторые довольно странные вещи в вашем коде:

  • Чтение и запись в файл одновременно затруднены. Ваш код, например, просто перепишет все существующие данные в файле.
  • Использование простого дескриптора файла (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";
  }
}

Другие вопросы по теме