4

各 GET/POST でタブ区切り形式の HTTP ヘッダー要求と応答データがあり、異なる行で応答します。このデータは、1 つの TCP フローに対して複数の GET、POST、および REPLY があるようなものです。これらのケースから最初の有効な GET - REPLY ペアのみを選択する必要があります。例(簡略化)は次のとおりです。

ID       Source    Dest    Bytes   Type   Content-Length  host               lines.... 
1         A         B       10     GET        NA          yahoo.com            2
1         A         B       10     REPLY      10          NA                   2 
2         C         D       40     GET        NA          google.com           4
2         C         D       40     REPLY      20          NA                   4
2         C         D       40     GET        NA          google.com           4
2         C         D       40     REPLY      30          NA                   4
3         A         B       250    POST       NA          mail.yahoo.com       5
3         A         B       250    REPLY      NA          NA                   5
3         A         B       250    REPLY      15          NA                   5
3         A         B       250    GET        NA          yimg.com             5
3         A         B       250    REPLY      35          NA                   5
4         G         H       415    REPLY      10          NA                   6
4         G         H       415    POST       NA          facebook.com         6
4         G         H       415    REPLY      NA          NA                   6
4         G         H       415    REPLY      NA          NA                   6
4         G         H       415    GET        NA          photos.facebook.com  6
4         G         H       415    REPLY      50          NA                   6

....

したがって、基本的には、ID ごとに 1 つの要求と応答のペアを取得し、それらを新しいファイルに書き込む必要があります。

'1' の場合は 1 組なので簡単です。しかし、両方の行が GET、POST、または REPLY である誤ったケースもあります。したがって、そのような場合は無視されます。

「2」の場合、最初の GET - REPLY ペアを選択します。

「3」の場合、最初の GET を選択しますが、最初の GET には Content-Length がないため、2 番目の REPLY を選択します (サブシークェスト REPLY がより適切な候補になります)。

「4」の場合、最初のヘッダーは REPLY にできないため、最初の POST (または GET) を選択します。2 番目の GET の後に REPLY を選択しませんが、POST の後にコンテンツの長さが欠落していても、REPLY はその後に来るためです。したがって、最初の REPLY を選択します。

したがって、最適な要求と応答のペアを選択したら、それらを 1 行でペアにする必要があります。たとえば、出力は次のようになります。

 ID       Source    Dest    Bytes   Type   Content-Length  host         .... 
   1         A         B       10     GET      10          yahoo.com
   2         C         D       40     GET      20          google.com
   3         A         B       250    POST     15          mail.yahoo.com
   4         G         H       415    POST     NA          facebook.com

実際のデータには他にも多くのヘッダーがありますが、この例は必要なものをほとんど示しています。Perl でこれを行うにはどうすればよいでしょうか。私はほとんど最初に立ち往生しているので、一度に1行しかファイルを読むことができませんでした.

open F, "<", "file.txt" || die "Cannot open $f: $!";

  while (<F>) {
    chomp;
    my @line = split /\t/;


      # get the valid pairs for cases with multiple request - replies


      # get the paired up data together

  }
  close (F);

*編集: 各 ID の HTTP ヘッダー行の数を示す列を追加しました。これは、チェックする後続の行数を知るのに役立つ場合があります。また、最初のヘッダー行が REPLY になるように ID '4' を変更しました。*

4

2 に答える 2

3

以下のプログラムは、私が必要だと思うことを行います。

コメントされており、かなり読みやすいと思います。不明な点は質問してください。

use strict;
use warnings;

use List::Util 'max';

my $file = $ARGV[0] // 'file.txt';
open my $fh, '<', $file or die qq(Unable to open "$file" for reading: $!);

# Read the field names from the first line to index the hashes
# Remember where the data in the file starts so we can get back here
#
my @fields = split ' ', <$fh>;
my $start = tell $fh;

# Build a format to print the accumulated data
# Create a hash that relates column headers to their widths
#
my @headers = qw/ ID Source Dest Bytes Type Content-Length host /;
my %len = map { $_ => length } @headers;

# Read through the file to find the maximum data width for each column
#
while (<$fh>) {
  my %data;
  @data{@fields} = split;
  next unless $data{ID} =~ /^\d/;
  $len{$_} = max($len{$_}, length $data{$_}) for @headers;
}

# Build a format string using the values calculated
#
my $format = join '   ', map sprintf('%%%ds', $_), @len{@headers};
$format .= "\n";

# Go back to the start of the data
# Print the column headers
#
seek $fh, $start, 0;
printf $format, @headers;

# Build transaction data hashes into $record and print them
# Ignore any events before the first request
# Ignore the second request and anything after it
# Update the stored Content-Length field if a value other than NA appears
#
my $record;
my $nreq = 0;

while (<$fh>) {

  my %data;
  @data{@fields} = split;
  my ($id, $type) = @data{ qw/ ID Type / };
  next unless $id =~ /^\d/;

  if ($record and $id ne $record->{ID}) {
    printf $format, @{$record}{@headers};
    undef $record;
    $nreq = 0;
  }

  if ($type eq 'GET' or $type eq 'POST') {
    $record = \%data if $nreq == 0;
    $nreq++;
  }
  elsif ($nreq == 1) {
    if ($record->{'Content-Length'} eq 'NA' and $data{'Content-Length'} ne 'NA') {
      $record->{'Content-Length'} = $data{'Content-Length'};
    }
  }
}

printf $format, @{$record}{@headers} if $record;

出力

質問で与えられたデータを使用して、このプログラムは生成します

ID   Source   Dest   Bytes    Type   Content-Length                  host
 1        A      B      10     GET               10             yahoo.com
 2        C      D      40     GET               20            google.com
 3        A      B     250    POST               15        mail.yahoo.com
 4        G      H     415    POST               NA          facebook.com
于 2012-05-02T14:33:25.303 に答える
1

これは、指定されたデータで機能するようです:

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

# Shape of input records
use constant ID       => 0;
use constant Source   => 1;
use constant Dest     => 2;
use constant Bytes    => 3;
use constant Type     => 4;
use constant Length   => 5;
use constant Host     => 6;

use constant fmt_head => "%-6s  %-6s  %-6s  %-6s  %-6s  %-6s  %s\n";
use constant fmt_data => "%-6d  %-6s  %-6s  % 6d  %-6s  % 6s  %s\n";

printf fmt_head, "ID", "Source", "Dest", "Bytes", "Type", "Length", "Host";

my @post_get;
my @reply;
my $lastid = -1;
my $pg_count = 0;

sub print_data
{
    # Final validity checking
    if ($lastid != -1)
    {
        printf fmt_data, $post_get[ID], $post_get[Source],
               $post_get[Dest], $post_get[Bytes], $post_get[Type], $reply[Length], $post_get[Host];
        # Reset arrays;
        @post_get = ();
        @reply = ();
        $pg_count = 0;
    }
}

while (<>)
{
    chomp;
    my @record = split;
    # Validate record here (number of fields, etc)
    # Detect change in ID
    print_data if ($record[ID] != $lastid);
    $lastid = $record[ID];

    if ($record[Type] eq "REPLY")
    {
        # Discard REPLY if there wasn't already a POST/GET
        next unless defined $post_get[ID];
        # Discard REPLY if there was a second POST/GET
        next if $pg_count > 1;
        @reply = @record if !defined $reply[ID];
        $reply[Length] = $record[Length]
                         if $reply[Length] eq "NA" && $record[Length] ne "NA";
    }
    else
    {
        $pg_count++;
        @post_get = @record if !defined $post_get[ID];
        $post_get[Length] = $record[Length]
                            if $post_get[Length] eq "NA" && $record[Length] ne "NA";
    }
}
print_data;

以下を生成します。

ID   Source   Dest   Bytes   Type   Content-Length             host
 1        A      B      10    GET               10        yahoo.com
 2        C      D      40    GET               20       google.com
 3        A      B     250   POST               15   mail.yahoo.com
 4        G      H     415   POST               NA     facebook.com

質問からの主な逸脱は、「Content-Length」を「Length」に置き換えたことです。必要に応じて修正は簡単です — と の 6 番目の長さfmt_datafmt_head14 に変更し、 に変更"Length""Content-Length"ます。

于 2012-04-29T16:58:48.027 に答える