【问题标题】:sed search multiple strings and output each string and its following string to a separate linesed 搜索多个字符串并将每个字符串及其后面的字符串输出到单独的行
【发布时间】:2013-09-09 21:41:54
【问题描述】:

例如; 我有一个长文件,其中包含:

Somestring anotherstring -xone xcont othertring -yone ycont againother \
-detail "detail Contents within quote" stuff morestuff .. 

Somestring anotherstring -xone xcont othertring -yone ycont againother \
morestrings -detail detailCont morestrings etc.. .. 

想要的输出:

-xone xcont
-ycont ycont
-detail "detail Contents withing quote" 

最好有一个 csv 文件:

xone yone detail
xcont ycont "detail Contents within quote"

获得所需输出的最佳方法是什么?我一直在尝试使用 sed 命令,但成功非常有限。我是 perl 的新手,所以也没有走得太远。请解释建议的解决方案。 提前致谢!

【问题讨论】:

  • 我不明白你在问什么。什么标识了您感兴趣的行?是从“Somestring anotherstring”开始的行吗?你所追求的信息是什么?在匹配行或下一行找到所有选项,例如 -xone-detail?并且输出只是诸如-xone 之类的“键”和诸如xcont 之类的值?处理双引号字符串会使事情变得复杂——转义嵌入式双引号的规则是什么? 'keys' 列表是固定在显示的三个(-xone-xcont-detail)还是任何破折号+名称字符串?
  • 我感兴趣的字符串前面有'-',后面的信息是我所追求的。不幸的是,每一行都不一样;有的长,有的短。是的,我感兴趣的密钥对是: -xone xcont -ycont ycont -detail "detail Contents withing quote"
  • OK:只是为了检查。您正在寻找任何字符串-xone 和以下字符串,或-yone 和以下字符串,或-detail 和以下双引号字符串,无需担心嵌入的双引号。此外,这些条目都应该在成对的相邻行上找到,并且组打印在一起。理想情况下,输出格式应该是 CSV(或者您想要制表符分隔值、TSV 还是其他格式方案?)。
  • 我遇到了我无法弄清楚的二年级错误:这就是我所做的:#!/usr/bin/perl use strict;使用警告;我的 $regex = qr/-(?\w+) \s+ (?: (?\w+) | "(?[^ "]+)" )/x; while () { while (/$regex/g) { say qq($+{key}: "$+{val}"); } } 我在哪里/如何阅读包含正在搜索的数据的文件?我收到两个不同的错误:序列 (?

标签: string perl sed


【解决方案1】:

这可能对你有用(GNU sed):

sed -r '/-(xone|yone|detail)/!d;s//\n\1/;s/[^\n]*\n//;s/\S+\s+("[^"]*"|\S+)/&\n/;P;D' file

这会查找包含字符串-xone-yone-detail 的行,并仅打印它们以及由" 或其他单词括起来的以下单词。

【讨论】:

  • 谢谢!。我收到以下错误:sed: -e expression #1, char 35: unknown command: `;'
【解决方案2】:

这个问题由两部分组成:

  1. 如何匹配标签
  2. 如何有序输出。

匹配部分非常简单,使用正则表达式。每个标签都是一个连字符减号,后跟一些单词字符。作为正则表达式模式:-\w+

该值似乎是一个单词(我们可以像\w+ 一样匹配)或一个带引号的字符串。假设这个字符串不能包含它的分隔符,我们可以使用"[^"]+",其中[^"] 是一个否定字符类,它匹配任何双引号字符。

我们如何结合这些?使用交替和命名捕获:

# I'll answer with Perl
my $regex = qr/-(?<key>\w+) \s+ (?: (?<val>\w+) | "(?<val>[^"]+)" )/x;

之后,$+{key} 包含键,$+{val} 包含该标签的值。我们现在可以提取一行中的所有标签。给定输入

Somestring anotherstring -xone xcont othertring -yone ycont againother \-detail "detail Contents within quote" stuff morestuff .. 
Somestring anotherstring -xone xcont othertring -yone ycont againother \morestrings -detail detailCont morestrings etc.. .. 

还有代码

use strict; use warnings; use feature 'say';
my $regex = ...;
while (<>) {
  while (/$regex/g) {
    say qq($+{key}: "$+{val}");
  }
}

我们得到输出

xone: "xcont"
yone: "ycont"
detail: "detail Contents within quote"
xone: "xcont"
yone: "ycont"
detail: "detailCont"

要以表格格式打印出来,我们必须以某种结构收集数据。我将假设每个标签可以在每一行出现一次。然后我们可以使用哈希来定义从标签到它们的值的映射。我们将这些哈希值收集在一个数组中,每行一个。我们还必须收集所有标题的名称,以防一行不包含所有标题。现在我们的代码变为:

use strict; use warnings; use feature 'say';
my $regex = ...;
my %headers;
my @rows;
while (<>) {
  my %tags;
  while (/$regex/g) {
    $tags{$+{key}} = $+{val};
  }
  push @rows, \%tags;
  @headers{keys %tags} = ();  # define the headers
}

现在我们如何打印数据呢?我们可以将它们转储为制表符分隔值:

my @headers = keys %headers;
say join "\t", map qq("$_"), @headers;
say join "\t", map qq("$_"), @$_{@headers} for @rows;

输出:

"yone"  "detail"        "xone"
"ycont" "detail Contents within quote"  "xcont"
"ycont" "detailCont"    "xcont"

哦,列的顺序是随机的。如果我们使用Text::CSV 模块,我们可以做得更好。那么:

use Text::CSV;

my @headers = keys %headers;
my $csv = Text::CSV->new({ eol => "\n" });
$csv->print(\*STDOUT, \@headers);
$csv->print(\*STDOUT, [@$_{@headers}]) for @rows;

我们得到输出:

yone,xone,detail
ycont,xcont,"detail Contents within quote"
ycont,xcont,detailCont

列的顺序仍然是随机的,但这可以通过排序来解决。

您可以通读Text::CSV documentation 以了解如何调整输出的许多选项。

【讨论】:

  • 你太有帮助了!我真的很感激!。这里唯一已知的变量是键(-xone、yone 和 -detail) 注意:这些变量前面都有 -。我不知道他们的价值观..
猜你喜欢
  • 1970-01-01
  • 2013-05-02
  • 1970-01-01
  • 2012-10-15
  • 1970-01-01
  • 2020-01-06
  • 2018-04-12
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多