假设我有一个名为courses.txt的文件,其内容如下:该文件有部分(课程提供者和我使用的电子邮件),后面是各种课程。例如:edX (anjan.fizz@gmail.com),然后是各种课程名称,每个课程前面都有序列号。
udemy (anjan.bar@gmail.com)
"=========================="-
1) foo bar
2) java programming language
3) redis stephen grider
4) javascript
5) react with typescript
6) kotlin
7) Etherium and Solidity : the Complete Developer's Guide
8) reactive programming with spring
coursera (anjan.foo@gmail.com)
"==========================-"
1) python
2) typescript
3) java concurrency
4) C#
edX (anjan.fizz@gmail.com)
"==========================-"
1) excel
2) scala
3) risk management
4) stock
5) oracle
6) mysql
7) java
==========================-
问题:我想申请一门课程,比如"java"。我想要一个匹配,它显示匹配的特定行(例如:"java")和相应的节名(例如,"edX (anjan.fizz@gmail.com)").
如果我想搜索"java"什么,regex"将给我以下匹配(我在windows上使用grep/perl):
<br>
udemy (anjan.bar@gmail.com)
2) java programming language
coursera (anjan.foo@gmail.com)
3) java concurrency
edX (anjan.fizz@gmail.com)
7) java
我试着向后看/向前看,但不知道如何打印课程提供者的名称、电子邮件和课程名称。
想法吗?
如果您以段落(由空白行分隔的文本块)进行处理,那么在每个段落中,匹配所需的模式是相当简单的-标题(后跟一行=
's)和一行java
perl -00 -wnE'say "$1n$2"
if /(.+?) n "=+.+? n .+? n ([^n]+sjavas[^n]+)/sx' file
(在Linux上测试;请继续阅读Windows版本。为便于阅读而分成几行。模式的解释见下文。)
在=
s行末尾,我使用.+?
而不是输入中跟随=
s的特定字符,因为您的样本输入不一致;它有-"
和"-
,在不同的段落。
由于这是在Windows上,您可能必须在一行代码中使用"
分隔符(我不知道您使用的是什么shell),因此您可能需要将模式中的文字"
替换为x22
(十六进制为"
),或您喜欢的其他序列。
希望对Windows有好处(现在不能在Windows上测试)
perl -00 -wnE "say qq($1n$2)
if /(.+?)n x22=+.+? n .+? n ([^n]+sjavas[^n]+) /sx
" file
-00开关使其按段落阅读。使用/x
修饰符,模式中的空格被忽略,因此我们可以使用它们来分隔内容以提高可读性。使用/s
修饰符,.
也匹配换行符。这对于中间的.+?
匹配多行很重要,直到java
(被空格包围)。†
如果您不介意使用脚本而不是一行代码,那么我推荐的是,例如
use warnings;
use strict;
use feature 'say';
local $/ = "nn";
while (<>) {
say "$1n$2"
if /(.+?) n "=+.+? n .+? n ([^n]+ sjavas [^n]+)/sx;
}
,& lt的在操作符逐行读取命令行上给出的文件,但是"line"之前设置为带有local $/ = "nn"
的段落。如果这是一个更大的程序的一部分,你不想改变整个程序的$/变量,那么这个局部就是存在的!
†或者,不使用使.
匹配换行符的/s
,而是使用多行模式
perl -00 -wnE'say "$1n$2"
if /(.+) n "=+.+ n (?:.+n)* (.+sjavas.+)/x' file
或者,如果你在Windows上需要"..."
,如
perl -00 -wnE "say qq($1n$2)
if /(.+) n x22=+.+ n (?:.+n)* (.+sjavas.+)/x' file
(同样,我现在无法在Windows上测试。)
请注意,现在我们不必像上面的/s
模式那样,用添加的?
(.+?
)使所有的.+
非贪婪——现在.+
停在换行符处,就像这里需要的那样。
或者,通过扩展模式动态使用/s
修饰符
perl -00 -wnE "say "$1n$2"
if /(.+) n x22=+.+ n (?s).+?(?-s) (.+sjavas.+)/x
" file
这里(?s)
"打开"/s
修饰符,它将一直有效到封闭组的末尾(在本例中是模式的其余部分),但(?-s)
将其关闭。
我不会给你一个完整的解决方案,但你可以从这个开始:
grep -iE "java|@" filename.txt
一些解释:
-i
使其不区分大小写-E
使用扩展正则表达式|
是这些扩展正则表达式的一个例子,它的意思是"OR":显示包含'java'或'@'(后者是所有的电子邮件地址)的行
结果,您得到一个包含所有电子邮件地址和所有'java'课程的文件,还有一个问题:如果电子邮件地址一行后面跟着另一行电子邮件地址,那么该地址没有'java'课程。因此,现在可以使用Perl删除下一行也是电子邮件地址的电子邮件地址。
查看输入数据,我们可以得出结论,该部分以包含电子邮件地址的一行开始。
数据从序列号开始。
基于这些信息,我们可以构建一个散列%sections
,其中line包含email作为键,并且所有以序列号开头的行都可以存储在键下的数组中。
构建哈希后,代码遍历所有节并查找包含搜索词的行,如果搜索词找到与行匹配的输出节。
注意:要在实际文件中工作,将<DATA>
替换为<>
,然后以./script.pl filename.dat
运行
use strict;
use warnings;
use feature 'say';
my($lookfor, %sections, $key);
$lookfor = shift || die "Provide search term";
while( <DATA> ) {
chomp;
$key = $_ if /@/;
push @{$sections{$key}}, $_ if /^d) /;
}
for my $section (keys %sections ) {
for( @{$sections{$section}} ) {
say "$sectionn"
. '-' x 30
. "n$_n" if /b$lookforb/i;
}
}
exit 0;
__DATA__
udemy (anjan.bar@gmail.com)
"=========================="-
1) foo bar
2) java programming language
3) redis stephen grider
4) javascript
5) react with typescript
6) kotlin
7) Etherium and Solidity : the Complete Developer's Guide
8) reactive programming with spring
coursera (anjan.foo@gmail.com)
"==========================-"
1) python
2) typescript
3) java concurrency
4) C#
edX (anjan.fizz@gmail.com)
"==========================-"
1) excel
2) scala
3) risk management
4) stock
5) oracle
6) mysql
7) java
==========================-
<br>
输出edX (anjan.fizz@gmail.com)
------------------------------
7) java
coursera (anjan.foo@gmail.com)
------------------------------
3) java concurrency
udemy (anjan.bar@gmail.com)
------------------------------
2) java programming language