【问题标题】:Extracting messages by format from text从文本中按格式提取消息
【发布时间】:2016-03-26 22:09:07
【问题描述】:

我有一堆看起来像这样的数据:

|ApplVerID=9|unknownID_9=184|MessageType=X|SenderCompID=CME|MsgSeqNum=131523|SendingTime=20140915014116175|TradeDate=20140915|NoMDEntries=1|MDUpdateAction=0|SecurityIDSource=8|SecurityID=951806|RptSeq=3|SecurityDesc=UD:1T: SG 0822951806|MDEntryType=2|MDEntryPx=21|MDEntrySize=1|MDEntryTime=14116000|TradeCondition=E|NetChgPrevDay=1|TradeID=1|TradeVolume=5|AggressorSide=2|MatchEventIndicator=1|unknownID_10=224
|ApplVerID=9|unknownID_9=272|MessageType=X|SenderCompID=CME|MsgSeqNum=131524|SendingTime=20140915014116175|TradeDate=20140915|NoMDEntries=2|MDUpdateAction=0|SecurityIDSource=8|SecurityID=265872|RptSeq=7|SecurityDesc=ONX4 P3400|MDEntryType=2|MDEntryPx=17|MDEntrySize=1|MDEntryTime=14116000|TradeCondition=1|NetChgPrevDay=0|TradeID=1|TradeVolume=1|AggressorSide=2|MDUpdateAction=0|SecurityIDSource=8|SecurityID=311863|RptSeq=8|SecurityDesc=ONX4 C4700|MDEntryType=2|MDEntryPx=9|MDEntrySize=1|MDEntryTime=14116000|TradeCondition=1|NetChgPrevDay=0|TradeID=1|TradeVolume=1|AggressorSide=2|unknownID_10=160
|ApplVerID=9|unknownID_9=411|MessageType=X|SenderCompID=CME|MsgSeqNum=131574|SendingTime=20140915020404771|TradeDate=20140915|NoMDEntries=4|MDUpdateAction=0|SecurityIDSource=8|SecurityID=873267|RptSeq=16|SecurityDesc=ONX4 P3600|MDEntryType=2|MDEntryPx=30|MDEntrySize=7|MDEntryTime=20404000|TickDirection=2|TradeCondition=E|NetChgPrevDay=-5|TradeID=1|TradeVolume=5|AggressorSide=1|MatchEventIndicator=1|MDUpdateAction=1|SecurityIDSource=8|SecurityID=873267|RptSeq=17|SecurityDesc=ONX4 P3600|MDEntryType=1|MDEntryPx=45|MDEntrySize=3|MDEntryTime=20404000|TradingSessionID=2|NumberOfOrders=1|MDPriceLevel=1|MDUpdateAction=0|SecurityIDSource=8|SecurityID=873267|RptSeq=18|SecurityDesc=ONX4 P3600|MDEntryType=7|MDEntryPx=41|MDEntryTime=20404000|MDUpdateAction=0|SecurityIDSource=8|SecurityID=873267|RptSeq=19|SecurityDesc=ONX4 P3600|MDEntryType=8|MDEntryPx=45|MDEntryTime=20404000|unknownID_10=043

我想提取每条包含“MDEntryType=2”的消息。请注意,每行中可以有多条消息。消息的数量在“NoMDEntries”中指示。还有其他不相关的消息,例如上面带有“MDEntryType=7”的消息。我想要的每条单独的消息都以 'MDUpdateAction=' 开头,并在下一个 'MDUpdateAction=' 或以 'unknownID_10=' 之前结束。

注意,每个区块有一个“SendingTime”。

我想按以下格式返回数据:

|SendingTime=20140915014116175|MDUpdateAction=0|SecurityIDSource=8|SecurityID=951806|RptSeq=3|SecurityDesc=UD:1T: SG 0822951806|MDEntryType=2|MDEntryPx=21|MDEntrySize=1|MDEntryTime=14116000|TradeCondition=E|NetChgPrevDay=1|TradeID=1|TradeVolume=5|AggressorSide=2|MatchEventIndicator=1|
|SendingTime=20140915014116175|MDUpdateAction=0|SecurityIDSource=8|SecurityID=265872|RptSeq=7|SecurityDesc=ONX4 P3400|MDEntryType=2|MDEntryPx=17|MDEntrySize=1|MDEntryTime=14116000|TradeCondition=1|NetChgPrevDay=0|TradeID=1|TradeVolume=1|AggressorSide=2|
|SendingTime=20140915014116175|MDUpdateAction=0|SecurityIDSource=8|SecurityID=311863|RptSeq=8|SecurityDesc=ONX4 C4700|MDEntryType=2|MDEntryPx=9|MDEntrySize=1|MDEntryTime=14116000|TradeCondition=1|NetChgPrevDay=0|TradeID=1|TradeVolume=1|AggressorSide=2|
|SendingTime=20140915020404771|MDUpdateAction=0|SecurityIDSource=8|SecurityID=873267|RptSeq=16|SecurityDesc=ONX4 P3600|MDEntryType=2|MDEntryPx=30|MDEntrySize=7|MDEntryTime=20404000|TickDirection=2|TradeCondition=E|NetChgPrevDay=-5|TradeID=1|TradeVolume=5|AggressorSide=1|MatchEventIndicator=1|

如果在每行的开头设置发送时间太困难,您可以将其关闭。我只是将数据按天分开,并使用“MDEntryTime”中记录的毫秒数。

我很乐意使用任何有效的软件解决方案(sed、perl 等)。我一直试图用 Emacs 宏解决这个问题,但无济于事。

感谢您提供的任何帮助。

编辑:有些行有两个“MDEntryType=2”实例,因此要从该行中提取两条消息。例如:

|ApplVerID=9|unknownID_9=293|MessageType=X|SenderCompID=CME|MsgSeqNum=153770|SendingTime=20140915113751112|TradeDate=20140915|NoMDEntries=2|MDUpdateAction=0|SecurityIDSource=8|SecurityID=292861|RptSeq=6|SecurityDesc=ONZ4 P4100|MDEntryType=2|MDEntryPx=151|MDEntrySize=6|MDEntryTime=113751000|TickDirection=2|TradeCondition=1|NetChgPrevDay=-12|TradeID=1|TradeVolume=5|AggressorSide=1|MDUpdateAction=0|SecurityIDSource=8|SecurityID=726692|RptSeq=7|SecurityDesc=ONV4 P4100|MDEntryType=2|MDEntryPx=276|MDEntrySize=5|MDEntryTime=113751000|TickDirection=2|TradeCondition=1|NetChgPrevDay=-13|TradeID=1|TradeVolume=5|AggressorSide=2|unknownID_10=153

通常,任何行都可以有任意数量的要提取的消息和要丢弃的消息。

【问题讨论】:

    标签: python regex perl awk sed


    【解决方案1】:

    awk 来救援!

    我只输出两个字段,其余的按你需要的顺序自己添加。

    $ awk 'BEGIN{FS=OFS="|"} /MDEntryType=2/ {print $7,$10}' file1 
    
    SendingTime=20140915014116175|MDUpdateAction=0
    SendingTime=20140915014116175|MDUpdateAction=0
    SendingTime=20140915020404771|MDUpdateAction=0
    

    如果您报告的字段很少,则最好将它们一一列出为 7 美元、10 美元等。如果您要打印几乎所有内容,可能会有更好的选择。

    我误解了您的问题,编辑后变得更加清晰。但是,您的输入/输出样本过于详细而没有增加任何价值。我根据您要解决的问题的本质设置了一个更简单的案例。

    我忽略了过滤要求,因为上面已经解决了模式匹配问题。

    假设你有这样的记录结构

    P1 PA1 PA2 C C1 C2 C3 C C1 C2 C3 C C1 C2 C3
    

    有一个带有属性的父记录和带有标题字段 C 和属性 C1、C2、C3 的子记录

    $ awk '{mtc=FS "C" FS; 
            n=split($0,a,mtc); 
            for(i=2;i<=n;i++) 
                print a[1] mtc a[i]}' file
    

    我们根据子头字段(前缀和后缀带字段分隔符)定义一个断点,根据这个值拆分记录,并在单独的行上打印父子对

    P1 PA1 PA2 C C1 C2 C3
    P1 PA1 PA2 C C1 C2 C3
    P1 PA1 PA2 C C1 C2 C3
    

    您还想只提取父属性的子集,假设是第二个,这里我们可以再次使用awk,但作为替代方案,我将使用cut 我们想要除第一个和第三个之外的所有字段,即

    $ awk ... | cut -d' ' -f1,3 --complement
    

    会给你结果

    PA1 C C1 C2 C3
    PA1 C C1 C2 C3
    PA1 C C1 C2 C3
    

    对于您的示例 FS 将是 |,子标题将是 MDUpdateAction=0

    【讨论】:

    • 谢谢!这很棒。如果一行上有两个 'MDEntryType=2' 实例,是否容易让它工作?不幸的是,我的示例每行只有一个,但数据中有很多行有两个。我将在上面的问题中添加一个示例。
    • 是的,它会寻找第一个匹配项,不管还有多少匹配项。但是,每一行将被视为一条记录。这不会将其一分为二。
    • 我正在尝试提取所有匹配项的数据。因此,如果有三个匹配项,则返回三组“MDEntryPx”,依此类推。如果您说它会这样做,我很乐意深入阅读 awk 手册。感谢您的帮助。
    • 如果您的要求更复杂,最好从awk 开始,您可以使用简洁的语法更好地控制转换和提取。当您拥有多个记录组时,您会做什么并不清楚。您的第三个输入行应该有 7,但输出只有一个。如果您可以根据您的需求创建一个干净的输入/输出对,而不需要丑陋的细节,那么您将得到更多答案,正如 Yoda 所说...
    • 这是一个可怕的答案;它几乎没有开始回答这个问题。它所做的只是提取SendingTime 字段和第一条消息的第一个字段,并且不会尝试将每条记录解压缩为问题中描述的多个消息。它也没有解释如何处理具有不同数量字段的消息。恐怕你得再努力一点
    【解决方案2】:

    这是一个 Perl 解决方案,它遍历每条记录中的所有字段并在变量 $st发送时间)、$nm消息数量)、$etype(当前消息的条目类型)和数组@message(当前消息内容

    字段被添加到@message,直到看到下一条消息的开始或unknownID_10,当消息的内容以及保存的发送时间被打印到输出并且@message被清空时

    此示例使用DATA 文件句柄直接从程序源文件本身读取。我相信您可以编写代码以从单独的输入文件中打开和读取?如果您只想在命令行上指定输入文件,那么您可以将&lt;DATA&gt; 更改为&lt;&gt;,一切都会为您工作

    #!/usr/bin/env perl
    
    use strict;
    use warnings 'all';
    
    while ( <DATA> ) {
    
        chomp;
        my @fields = /[^|\s]+/g;
    
        my ( $st, $nm, $etype );
        my @message;
    
        for my $f ( @fields ) {
    
            if ( $f =~ /SendingTime/ ) {
                $st = $f;
            }
            elsif ( $f =~ /NoMDEntries=(\d+)/ ) {
                $nm = $1;
            }
            elsif ( $f =~ /MDUpdateAction|unknownID_10/ ) {
                if ( @message and defined $etype and $etype == 2 ) {
                    print "|", join("|", $st, @message), "|\n";
                }
                @message = ($f);
                $etype = undef;
            }
            elsif ( defined $nm ) {
                $etype = $1 if $f =~ /MDEntryType=(\d+)/;
                push @message, $f;
            }
        }
    }
    
    __DATA__
    |ApplVerID=9|unknownID_9=184|MessageType=X|SenderCompID=CME|MsgSeqNum=131523|SendingTime=20140915014116175|TradeDate=20140915|NoMDEntries=1|MDUpdateAction=0|SecurityIDSource=8|SecurityID=951806|RptSeq=3|SecurityDesc=UD:1T: SG 0822951806|MDEntryType=2|MDEntryPx=21|MDEntrySize=1|MDEntryTime=14116000|TradeCondition=E|NetChgPrevDay=1|TradeID=1|TradeVolume=5|AggressorSide=2|MatchEventIndicator=1|unknownID_10=224
    |ApplVerID=9|unknownID_9=272|MessageType=X|SenderCompID=CME|MsgSeqNum=131524|SendingTime=20140915014116175|TradeDate=20140915|NoMDEntries=2|MDUpdateAction=0|SecurityIDSource=8|SecurityID=265872|RptSeq=7|SecurityDesc=ONX4 P3400|MDEntryType=2|MDEntryPx=17|MDEntrySize=1|MDEntryTime=14116000|TradeCondition=1|NetChgPrevDay=0|TradeID=1|TradeVolume=1|AggressorSide=2|MDUpdateAction=0|SecurityIDSource=8|SecurityID=311863|RptSeq=8|SecurityDesc=ONX4 C4700|MDEntryType=2|MDEntryPx=9|MDEntrySize=1|MDEntryTime=14116000|TradeCondition=1|NetChgPrevDay=0|TradeID=1|TradeVolume=1|AggressorSide=2|unknownID_10=160
    |ApplVerID=9|unknownID_9=411|MessageType=X|SenderCompID=CME|MsgSeqNum=131574|SendingTime=20140915020404771|TradeDate=20140915|NoMDEntries=4|MDUpdateAction=0|SecurityIDSource=8|SecurityID=873267|RptSeq=16|SecurityDesc=ONX4 P3600|MDEntryType=2|MDEntryPx=30|MDEntrySize=7|MDEntryTime=20404000|TickDirection=2|TradeCondition=E|NetChgPrevDay=-5|TradeID=1|TradeVolume=5|AggressorSide=1|MatchEventIndicator=1|MDUpdateAction=1|SecurityIDSource=8|SecurityID=873267|RptSeq=17|SecurityDesc=ONX4 P3600|MDEntryType=1|MDEntryPx=45|MDEntrySize=3|MDEntryTime=20404000|TradingSessionID=2|NumberOfOrders=1|MDPriceLevel=1|MDUpdateAction=0|SecurityIDSource=8|SecurityID=873267|RptSeq=18|SecurityDesc=ONX4 P3600|MDEntryType=7|MDEntryPx=41|MDEntryTime=20404000|MDUpdateAction=0|SecurityIDSource=8|SecurityID=873267|RptSeq=19|SecurityDesc=ONX4 P3600|MDEntryType=8|MDEntryPx=45|MDEntryTime=20404000|unknownID_10=043
    

    输出

    |SendingTime=20140915014116175|MDUpdateAction=0|SecurityIDSource=8|SecurityID=951806|RptSeq=3|SecurityDesc=UD:1T: SG 0822951806|MDEntryType=2|MDEntryPx=21|MDEntrySize=1|MDEntryTime=14116000|TradeCondition=E|NetChgPrevDay=1|TradeID=1|TradeVolume=5|AggressorSide=2|MatchEventIndicator=1|
    |SendingTime=20140915014116175|MDUpdateAction=0|SecurityIDSource=8|SecurityID=265872|RptSeq=7|SecurityDesc=ONX4 P3400|MDEntryType=2|MDEntryPx=17|MDEntrySize=1|MDEntryTime=14116000|TradeCondition=1|NetChgPrevDay=0|TradeID=1|TradeVolume=1|AggressorSide=2|
    |SendingTime=20140915014116175|MDUpdateAction=0|SecurityIDSource=8|SecurityID=311863|RptSeq=8|SecurityDesc=ONX4 C4700|MDEntryType=2|MDEntryPx=9|MDEntrySize=1|MDEntryTime=14116000|TradeCondition=1|NetChgPrevDay=0|TradeID=1|TradeVolume=1|AggressorSide=2|
    |SendingTime=20140915020404771|MDUpdateAction=0|SecurityIDSource=8|SecurityID=873267|RptSeq=16|SecurityDesc=ONX4 P3600|MDEntryType=2|MDEntryPx=30|MDEntrySize=7|MDEntryTime=20404000|TickDirection=2|TradeCondition=E|NetChgPrevDay=-5|TradeID=1|TradeVolume=5|AggressorSide=1|MatchEventIndicator=1|
    

    【讨论】:

    • 这看起来很完美!谢谢。数据位于本地文件(每个约 4GB)中,我一直使用 cat 将这些文件导入 sed 脚本,并使用 &gt; 将输出定向到本地文件。我很高兴编写一些东西来将数据传输到您的脚本中,但是您的脚本是用什么语言编写的,perl?
    • @MattBrigida:是的,这是 Perl。您需要做的就是写perl etype2.pl infile.txt &gt; outfile.txt,其中etype2.pl 是Perl 源文件的名称
    • 顺便说一下,sed 和 perl(以及绝大多数命令行实用程序)都将接受输入文件参数,并且无需从 cat 中通过管道输入它们。见The Useless Use of Cat Award
    • 搞定了!再次感谢。非常感谢您的帮助。并感谢 cat 奖的链接 - 一个很好的(和有趣的)方式来帮助像我这样的人在命令行中更合适。
    猜你喜欢
    • 2021-12-07
    • 2015-05-10
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-10-28
    • 2012-05-04
    • 1970-01-01
    相关资源
    最近更新 更多