【问题标题】:Loop through text file & find strings to append to children strings in hierarchically related text file遍历文本文件并查找字符串以附加到分层相关文本文件中的子字符串
【发布时间】:2016-07-21 08:37:08
【问题描述】:

我有许多包含分层相关字符串的文本文件。

编辑这是一个例子。这些文件基本上如下所示

HEBV000000000000
HEH11111111  2222222022HCPP      3333        0000 AAA
HET11111  22222222222222
HEH888888  3333333333HCPP      3333        0000 AAA
HET2222  33333333333333333
HEH44444444 55555555HCPP      4444      0000 BBB
HET555555  0066666666666666666
HEE0019000000090
HEBV0120150301429
HEH5588558888 5555000044440000NCPP       164201503010000 HIP
HER9999A 0157000120150303333
HET8888B 0036400120150303333
HEE0044000000040

细分为:

HEB (start of batch1)
HEH (start of group1)
HET (end of group1)
HEH (start of group2)
HET (end of group2)
HEH (start of group3)
HET (end of group3)
HEE (end of batch1)
HEB (start of batch 2)
HEH (start of group1)
HER (start of subgroup1)
HET (end of group1)
HEE (end of batch2)

字符串以几种方式相关:

HEB 字符串表示批次的开始。 HEE 详细列出了上一批中的 HEH、HER 和 HET 记录数。

下一个关系是文档的核心,即 HER 和 HET 记录与单个 HEH 相关。批次(HEB 到 HEE)可以包含多个 HEH-HER-HET 组。一批中总会有至少一个 HEH-HET 组;可能有很多。如果存在 HER 记录,则它与它之前的 HEH 以及它之后的所有 HET 相关,直到遇到新的 HER 或 HEH。因此 HER 和 HET 记录只与单个 HEH 记录相关,但 HEH 记录可以与多个 HER 和 HET 记录相关联。

任何字符串中都没有链接标识符。唯一可用的关系是文件中字符串的位置。 (不是我做的,我无法改变)

我想做的是有一个基于 HET 记录的输出文件,如下所示,以便我可以导入到统计数据包中(请注意,我的意思是从每一行打印整个字符串,但对于易于阅读我只是显示字符串的 HE*):

HET1 HEH1 HEB FILENAME HEE
HET2 HEH2 HER2 HEB FILENAME HEE
HET3 HEH3 HER3 HEB FILENAME HEE

等等。

我认为它会如何工作的逻辑是:

Read in the file
Get filename and append to HEB records

Then
Look for HEB record and store
Look for HEE record and store
Append HEB and HEE to HEH
  if new HEB is found repeat above until end of file

Then

Look for HEH record and store
Append to HET records until an HEH or HEE record is found
if a new HEH is found, append it to HET records until HEH or HEE is found
repeat until eof

Then

Look for HER record and store
Append to HET records until an HER, HEH or HEE record is found
if a new HER is found, append it to HET records until an HER, HEH or HEE record is found
repeat until eof
save to new file

我认为这会让我进入

HET1 HEH1 HEB FILENAME HEE
HET2 HEH2 HER2 HEB FILENAME HEE
HET3 HEH3 HER3 HEB FILENAME HEE

我之前对类似格式的文件提出了类似的问题:

Bash: loop through file line by line, find specific string and append to each subsequent line until same string is found

bash & awk: Loop through dir running two separate awk commands on all files and saving in new dir

不幸的是,由于多层关系,这些文件更加复杂。这超出了我从其他问题修改解决方案的能力

代码:

for f in *txt
do
    awk '/^AB1/{ab1=$0;next}/^AB2/{print $1, $2, ab1}' "$f" > "new$f"
    awk '{print $1,$2,$3,$4,$5,$6,FILENAME}' "new$f" > "newnew$f"
done

我不知道在这里 awk 是否是一个好主意,或者像 Perl 或 Java 这样的东西是否会更好。正如我在其他问题中提到的那样,我是一名医生,而不是程序员,虽然我可以通过一些理解来修改代码(通常是绊倒我的脚),但当我遇到这样的事情时,我发现自己远远超出了我的深度.

【问题讨论】:

  • 我自己会在perl 做这件事——但我也很难听懂你的问题。你能提供一些样本(经过清理的)输入和所需的输出吗?
  • 感谢您的回复。该文件看起来像问题中的第一个块,即:`HEB 行然后 HEH 行然后 HET 行,字符串更长,但每行的开头是 HEB、HEH、HER、HET 或 HEE。就是这样,一直到文件。
  • “一个 HEH 记录可以与多个 HER 和 HET 记录相关联。” 您确定吗?如果 HEH 和 HET 记录以与 HEB 和 HEE 记录相同的方式配对,则数据格式更有意义。我可以理解可能有多个 HER 记录,按照 HEH HER HER HER HER HET 之类的顺序,但您说的也可能有多个 HET 记录,例如 HEH HER HET HER HET HER HER HET
  • As Sobrique has asked, “你能给出一些样本(经过清理的)输入和所需的输出吗?”从样本输入中查看您期望的确切输出
  • 第一批代码是一个实际的文件输出,保存了一些数字和字母的更改以使其脱敏。 HEB 是批次标题。 HEE 是批处理页脚。 HER 与前面的 HEH 绝对相关,如果出现 HEH,HER 总是跟随在 HEH 之后。通常只有 HEH 和 HET 记录出现在一个批次中,但 HER 记录的罕见情况只会为该 HEH 添加更多信息。 HEH 是一个帐户规范字符串。 HET 记录是属于它/它们之前的 HEH 下的项目。

标签: java macos perl awk


【解决方案1】:

我认为这会如你所愿,但你的描述有点不透明

  • 程序会跟踪最近的 HEB 和 HEH 记录的值,以及自上一个 HEH 或 HET 以来的所有 HER 记录

  • 我已经使用了输入文件中每一行的第一个字段。不清楚这是否足够,或者您是否需要整行的数据

  • 每当遇到 HET 时,输出记录的内容都会保存在数组 @records 中,但此时它们缺少 HEE 信息,因此无法打印

  • 在每一条HEE记录中,所有等待输出与当前记录的值一起打印,等待列表清空

  • 请注意,我已对您自己的示例中的输入稍作更改,以允许每个 HEH 有多个 HET 记录,每个 HET 有多个 HER 记录

  • @ARGV = 'f1.txt' 行模拟命令行上的一个参数,就好像你输入了perl process_data.pl f1.txt。您应该在使用代码之前删除此行,并且预期的方法是使用 glob 模式作为参数,以便 shell 找到所有相关文件并将它们传递给代码


use strict;
use warnings 'all';
use feature 'state';

@ARGV = 'f1.txt';

my ( $heb, $heh, @her );

my @records;

while ( <> ) {

    my ($item) = split;

    die unless my ($type) = $item =~ /^(HE[BHRTE])/;

    state $dispatch = {
        HEB => sub {
            $heb = shift;
            $heh = undef;
            @her = ();
        },
        HEH => sub {
            $heh = shift;
            @her = ();
        },
        HER => sub {
            push @her, shift;
        },
        HET => sub {
            my $het      = shift;
            my $filename = $ARGV;
            push @records, [ $het, $heh, @her, $heb, $filename ];
            @her = ();
        },
        HEE => sub {

            my $hee = shift;

            for my $rec (@records) {
                push @$rec, $hee;
                print "@$rec\n";
            }

            $heb = $heh = undef;
            @her = ();
            @records = ();
        },
    };

    $dispatch->{$type}->($item);
}

输入

HEBV000000000000
HEH11111111  2222222022HCPP      3333        0000 AAA
HET11111  22222222222222
HEH888888  3333333333HCPP      3333        0000 AAA
HET2222  33333333333333333
HEH44444444 55555555HCPP      4444      0000 BBB
HET555555  0066666666666666666
HEE0019000000090
HEBV0120150301429
HEH5588558888 5555000044440000NCPP       164201503010000 HIP
HER9999A 0157000120150303333
HER9999B 0157000120150303333
HET8888B 0036400120150303333
HER9999C 0157000120150303333
HER9999D 0157000120150303333
HET8888B 0036400120150303333
HEE0044000000040

输出

HET11111 HEH11111111 HEBV000000000000 f1.txt HEE0019000000090
HET2222 HEH888888 HEBV000000000000 f1.txt HEE0019000000090
HET555555 HEH44444444 HEBV000000000000 f1.txt HEE0019000000090
HET8888B HEH5588558888 HER9999A HER9999B HEBV0120150301429 f1.txt HEE0044000000040
HET8888B HEH5588558888 HER9999C HER9999D HEBV0120150301429 f1.txt HEE0044000000040

【讨论】:

  • 感谢您花时间回答这个问题。你的输出看起来很完美。我要试一试,然后回来报告。非常感谢。
  • 这是完美的。非常感谢。我可以用类似于 `for f in *.txt do hfile.pl "$f" > "new$f"' 的东西替换 @ARGV 吗?我将不得不制定 perl 语法。这就是我使用 awk 运行某些东西的方式。
  • @FocusedEnergy:这就是我最后一个要点的意思。如果您使用perl process_data.pl *.txt,您可以让shell 为您扩展文件列表。这样,您无需编辑程序即可更改正在处理的文件。请注意,您必须删除@ARGV = 行,否则它将覆盖命令行上传递的任何内容。如果您真的想在代码中执行此操作,则可以将其更改为 @ARGV = glob '*.txt'
【解决方案2】:

恐怕您的文件实际上是什么样子的并不完全清楚 - 但如果您逐行关注,您可以更轻松地做到这一点线处理。

特别有两个技巧 - 第一个使用 $/ 设置记录分隔符,然后分块读取文件。

例如:

#!/usr/bin/perl
use strict;
use warnings;

local $/ = 'HEE'; 

while ( <DATA> ) {
    print "\nStart of Record\n";
    print;
   print "\nEnd of Record\n";
}

__DATA__
HEB (start of batch1)                       
HEH (start of group1)     
HET                                      
HET                                               
HET (end of group1)                                                                                                                               
HEH (start of group2)
HET
HET (end of group2)
HEE (end of batch1)

每次遇到文件中的“HEB”标记时,都会执行循环。此时,您可以应用正则表达式匹配来提取子元素。所以看看它 - HEH 分隔子记录:

 my @groups = m/^(HEH .*?(?=HE[HE]))/gms;
   foreach my $group ( @groups ) { 
        print "Start of group:\n";
        print $group;
        print "End of group\n";
   }

这使用正则表达式和零宽度模式来捕获“HEH”和“HEH”或“HEE”之间的文本块:

Start of group:
HEH (start of group1)     
HET                                      
HET                                               
HET (end of group1)                                                                                                                               
End of group
Start of group:
HEH (start of group2)
HET
HET (end of group2)
End of group

结合这两种技术,你应该能够选择你想要的东西到你的记录中。恐怕我不能给你一个更详细的例子,因为我真的需要一个更完整的例子输入和输出。

#!/usr/bin/perl
use strict;
use warnings;

use Data::Dumper;

local $/ = 'HEE'; 

while ( <DATA> ) {
    print "\nStart of Record\n";
    print;
   print "\nEnd of Record\n";

   my @groups = m/^(HEH .*?(?=HE[HE]))/gms;
   foreach my $group ( @groups ) { 
        print "Start of group:\n";
        print $group;
        print "End of group\n";
        my @HET = $group =~ m/HET (.*)$/gm; 
        print "HET lines: \n";
        print join "\n", @HET,"\n";
   }
}

__DATA__
HEB (start of batch1)                       
HEH (start of group1)     
HET                                      
HET                                               
HET (end of group1)                                                                                                                               
HEH (start of group2)
HET
HET (end of group2)
HEE (end of batch1)
HEB (start of batch 2)
HEH (start of group1)            
HER (start of subgroup1)                                
HET                                     
HET                                                
HER (start of subgroup2)                                     
HET                                            
HEH (start of group2)                               
HET (end of group2)                              
HEE (end of batch 2)

【讨论】:

  • 非常感谢您的详细回复。我已经编辑了我的原始问题以显示文本文件的实际示例,我将尝试使用您的建议,看看我是否可以让它正常工作。
【解决方案3】:

承认不理解这种格式和目的(以及提出这个的人的心态)并假设它具有一致的结构,这可能会起作用

$ awk '/HER/{r++;m=0} 
      !m&&/HET/{m++; t++; 
        print $1 t, "HEH" t, (r?"HER"t OFS:"") "HEB", FILENAME, "HEE";next}' heb

HET1 HEH1 HEB heb HEE
HET2 HEH2 HER2 HEB heb HEE
HET3 HEH3 HER3 HEB heb HEE

【讨论】:

  • 感谢您抽出宝贵时间回复。我会测试你的建议。格式是一致的,文件中的每一行都以 HE* 开头。字符串更长,但它们总是以这种方式开始。我想吸出最终文件,以便在统计程序中使用它。
【解决方案4】:

如果您想使用 awk 执行此操作:

gawk -v RS="HEB" '{
    for(i=2;i<NF;i++){
        if( $i ~ /^HE[R|H]/){
            x=x" "$i
        };
        if( $i ~ /^HET/ ){
            print $i""x,"HEB"$1,FILENAME,$NF;x=""
        }
    }
}' file.txt

【讨论】:

    猜你喜欢
    • 2014-05-02
    • 1970-01-01
    • 1970-01-01
    • 2021-12-27
    • 1970-01-01
    • 2015-07-14
    • 1970-01-01
    • 2017-04-22
    相关资源
    最近更新 更多