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
Стоит ли изучать PHP в 2023-2024 годах?
Стоит ли изучать PHP в 2023-2024 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
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";
  }
}

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