将行添加到文本文件中。

huangapple go评论56阅读模式
英文:

perl add line into text file

问题

你的代码几乎正确,但有一些小问题需要修复。以下是修复后的代码:

use strict;
use warnings;
my $config = "filename.txt";
open (CONFIG, "+<$config") or die "Fail to open config file $config\n";
while (<CONFIG>) {
    if ($_ =~ /^apple$/) {
        print CONFIG "apple\n";
        print CONFIG "\ttesting\n";
    }
}
close CONFIG;

在这个修复后的代码中,主要有以下几点改进:

  1. $apple 改为 "apple",以匹配字符串 "apple"。
  2. 在匹配 "apple" 后,首先写入 "apple",然后使用 \t 添加一个制表符,然后写入 "testing"。
  3. 删除了 chop;,因为它会移除每行的换行符,你想要保留这些换行符。

这样,你的脚本应该能够按照期望的方式在匹配字符串 "apple" 后添加新行和制表符。

英文:

I am writing a script to append text file by adding some text under specific string in the file after tab spacing. Help needed to add new line and tab spacing after matched string "apple" in below case.

Example File:

apple
&lt;tab_spacing&gt;original text1
orange
&lt;tab_spacing&gt;original text2

Expected output:

apple
&lt;tab_spacing&gt;testing
&lt;tab_spacing&gt;original text1
orange
&lt;tab_spacing&gt;original text2

What i have tried:

use strict;
use warnings;
my $config=&quot;filename.txt&quot;;
open (CONFIG,&quot;+&lt;$config&quot;) or die &quot;Fail to open config file $config\n&quot;;
	while (&lt;CONFIG&gt;) {
		chop;
		if (($_ =~ /^$apple$/)){
			print CONFIG &quot;\n&quot;;
			print CONFIG &quot;testing\n&quot;;
		}
}
      close CONFIG;

答案1

得分: 3

我们不能简单地在文件的中间进行“添加”文本的尝试。文件是一系列字节,除了在末尾以外,无法添加或删除它们,只能更改它们。因此,如果我们开始写入文件的中间,我们正在更改那里的字节,从而覆盖了其后的内容。相反,我们必须复制剩余的文本并在“添加”后写入它,或者在复制文件时添加文本。

另一种方法是将整个文件读入字符串中,然后对其运行正则表达式以进行更改,然后写出新字符串。假设文件不太大。

perl -0777 -pe's{apple\n\K(\t)}{Added text\n$1}g' in.txt

-0777开关使其将整个文件读入字符串中("slurp"),在$_中可用,默认情况下正则表达式与其绑定。\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()构造函数

my $file_content = path($file)->slurp;  # 将文件读入字符串中

# 现在使用正则表达式;上面的所有讨论都适用
$file_content =~ s{apple\n\K(?=\t)}{Added text\n}g;

# 打印$file_content,以便重定向等。或者将其写入文件
path($new_file)->spew($file_content);

我使用库Path::Tiny将文件"slurp"到字符串中,使用spew$file_content写入新文件。这需要安装,因为它不是Perl的“核心”(通常不随Perl一起安装),如果由于某种奇怪的原因这是一个问题,这里有一个没有任何库的解决方法:

my $file_content = do { 
    local $/;
    open my $fh, '<', $file or die "无法打开 $file: $!";
    <$fh>;
};

甚至可以使用以下方法:

my $file_content = do { local (@ARGV, $/) = $file; <> };

(参见这个帖子以获取一些解释和参考资料)。

英文:

We cannot simply "add" text to a middle of a file as attempted. A file is a sequence of bytes and one cannot add or remove them (except at the end) but only change them. So if we start writing to a middle of a file then we are changing the bytes there, so overwriting what follows that place. Instead, we have to copy the rest of the text and write it back following the "addition," or to copy the file adding text in the process.

Yet another way is to read the whole file into a string and run a regex on it to change it, then write out the new string. Assuming that the file isn't too large for that

perl -0777 -pe&#39;s{apple\n\K(\t)}{Added text\n$1}g&#39; in.txt

The -0777 switch makes it read the whole file into a string ("slurp" it), available in $_, to which the regex is bound by default. That \K, which is a lookbehind, drops previous matches so they are not consumed out of the string and we don't have to (capture and) put them back. With the /g modifier it keeps going through the whole string, to find and change all occurrences of the pattern.

This prints the changed file to screen, what can be saved in a new file by redirecting it

perl -0777 -pe&#39;...&#39; in.txt &gt; out.txt

Or, one can change the input file "in place" with -i

perl -0777 -i.bak -pe&#39;...&#39; in.txt 

The .bak makes it save the original with .bak extension. See switches in perlrun.

Another way is to use a lookahead for what follows (the tab) so that we don't have to capture and put it back

perl -0777 -pe&#39;s{apple\n\K(?=\t)}{Added text\n}g&#39; in.txt

All of these produce the desired change.


Note on that tab ("tab_spacing")

The regex above assumes a tab character at the beginning of the line following the line with apple. When we say "tab" we mean one (tab) character.

But there are many reasons why there may in fact not be a tab character, even if it looks just like there is one. An example: all tabs may be automatically replaced by spaces by an editor.

So it may be safer to use \s+ (multiple spaces) instead of \t in the regex

s{apple\n\K(\s+)}{Added text\n$1}g

or

s{apple\n\K(?=\s+)}{Added text\n}g

If this is to be done inside of an existing larger Perl program (and not as a command-line program, "one-liner," as above), one way

use Path::Tiny;  # path(), constructor

my $file_content = path($file)-&gt;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)-&gt;spew($file_content);

I use the library Path::Tiny to "slurp" the file into a string and spew to write $file_content to a new file. That need be installed as it is not in a "core" (doesn't usually come installed with Perl), and if that is a problem for some strange reason here is an idiom-of-sorts for it without any libraries

my $file_content = do { 
    local $/;
    open my $fh, &#39;&lt;&#39;, $file or die &quot;Can&#39;t open $file: $!&quot;;
    &lt;$fh&gt;;
};

or even

my $file_content = do { local (@ARGV, $/) = $file; &lt;&gt; };

(see this post for some explanation and references)

答案2

得分: 2

以下是翻译的内容:

  • 你的代码中有一些相当奇怪的东西,说实话:
  • 同时从文件中读取和写入很困难。例如,你的代码只会覆盖文件中的现有数据。
  • 使用裸字文件句柄(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";
  }
}
英文:

Some pretty weird stuff in your code, to be honest:

  • Reading from and writing to a file at the same time is hard. Your code, for example, would just write all over the existing data in the file
  • Using a bareword filehandle (CONFIG) instead of a lexical variable and two-arg open() instead of the three-arg version (open my $config_fh, &#39;+&lt;&#39;, $config&#39;) makes me think you're using some pretty old Perl tutorials
  • Using chop() instead of chomp() makes me think you're using some ancient Perl tutorials
  • You seem to have an extra $ in your regex - ^$apple$ should probably be ^apple$

Also, Tie::File has been included with Perl's standard library for over twenty years and would make this task far easier.

#!/usr/bin/perl

use strict;
use warnings;

use Tie::File;

tie my @file, &#39;Tie::File&#39;, &#39;filename.txt&#39; or die $!;

for (0 .. $#file) {
  if ($file[$_] eq &#39;apple&#39;) {
    splice @file, $_ + 1, 0, &quot;\ttesting\n&quot;;
  }
}

答案3

得分: 1

以下是翻译好的部分,代码部分已经忽略:

"tab spacing" 不是很清楚你的意思,但你可能正在寻找:

perl -pE 'm/^(\t*)/; say "testing" if $a; $a = /apple/' filename.txt

我怀疑你实际上想要使用 \s 而不是 \t,但具体情况可能有所不同。基本上,在每行输入上,你匹配前导空格,然后如果前一行匹配,打印带有该空格和字符串 'testing' 的行。

要详细编写它:

#!/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*)/;    # 可能更倾向于使用 \s 而不是 \t
    say "testing" if $a;
    $a = /apple/;
    print;
}
英文:

It's not entirely clear what you mean by "tab spacing", but you might be looking for:

perl -pE &#39;m/^(\t*)/; say &quot;testing&quot; if $a; $a = /apple/&#39; filename.txt

I suspect you actually want \s instead of \t, but YMMV. Basically, on each line of input, you match the leading whitespace and then print a line with that whitespace and the string 'testing' if the previous line matched.

To write it verbosely:

#!/usr/bin/env perl

use 5.12.0;
use strict;
use warnings;
my $n = &#39;filename.txt&#39;;

open my $f, &#39;&lt;&#39;, $n, or die &quot;$n: $!\n&quot;;
while(&lt;$f&gt;){
        m/^(\t*)/;    # possibly \s is preferred over \t
        say &quot;testing&quot; if $a;
        $a = /apple/;
        print;
}

huangapple
  • 本文由 发表于 2023年2月16日 11:35:25
  • 转载请务必保留本文链接:https://go.coder-hub.com/75467608.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定