2014-08-27 3 views
0

У меня вопрос. Я хотел бы написать perl-скрипт для анализа вывода Mailgun в формате csv. Я бы предположил, что функции «split» и «join» будут работать правильно для этой процедуры. Вот некоторые примерные данные:mailgun отчет в формат csv perl

Примеры данных

{ 

    "geolocation": { 

    "city": "Random City", 

    "region": "State", 

    "country": "US" 
    }, 
    "url": "https://www4.website.com/register/1234567", 

    "timestamp": "1237854980723.0239847" 
} 


{ 

    "geolocation": { 

    "city": "Random City2", 

    "region": "State2", 

    "country": "mEXICO" 
    }, 
    "url": "https://www4.website2.com/register/ABCDE567", 

    "timestamp": "1237854980723.0239847" 
} 

Желаемая Выход

"город", "регион", "страна", "URL", "метка времени"

"Random City", "State", "US", "https://www4.website.com/register/1234567", "1237854980723.0239847"

"Random City_2", "State_2", "mEXICO", "www4.website2.com/ABCDE567","1237854980723.0239847_2"

Моя цель - взять данные моего образца и создать нужный результат в виде CSV с разделителями-запятыми файл. Я не совсем уверен, как это сделать. Обычно я пытаюсь взломать это с помощью серии однострочных в пакетном файле, но я бы предпочел сценарий perl. Реальные данные будут содержать больше информации. Однако просто выяснить, как разбирать общую структуру, будет хорошо.

Вот что у меня есть в пакетном файле.

Код

perl -p -i.bak -e "s/(,$|,+ +$|^.*?{$|^.*?}.*?$|^.*?],.*?$)//gi" file.txt 

    rem Removes all unnecessary characters and lines with { and }.^

    perl -p -i.bak -e "s/(^ +| +$)//gi" file.txt  

    perl -p -i.bak -e "s/^\n$//gi" file.txt 


rem Removes all blank lines in initial file. Next one-liner takes care of trailing and beginning 

rem whitespace. The file is nice and clean now. 

perl -p -e "s/(^\".*?\"):.*?$/$1/gi" file.txt > header.txt 

rem retains only header info and puts into 'header.txt'^

perl -p -e "s/^\".*?\": +(\".*?\"$)/$1/gi" file.txt > data.txt 

rem retains only data that is associated with each field. 

perl -p -i.bak -e "s/\n/,/gi" data.txt 

rem replaces new line character with ',' delimiter. 

perl -p -i.bak -e "s/^/\n/gi" data.txt 

rem drops data down a line 

perl -p -i.bak -e "s/\n/,/gi" header.txt 

rem replaces new line character with ',' delimiter. 

copy header.txt+data.txt report.txt 

rem copies both files together. Since there is the same amount of fields as there are data 

rem delimiters, the columns and headers match. 

Мой выход

"город", "регион", "страна", "URL", "метка времени"

"Random City", «Государство», «США», «https://www4.website.com/register/1234567», 1237854980723.0239847

Это делает трюк, но conde nsed скрипт будет лучше. Меняющиеся ситуации могут повлиять на этот пакетный скрипт. Мне нужно что-то более твердое. Какие-либо предложения??

+2

использовать [JSON] (https://metacpan.org/pod/JSON). – jm666

ответ

1

Вы можете использовать один скрипт на Perl с одним регулярным выражением

#!/usr/bin/env perl 
use v5.10; 
use Data::Dumper; 

$_ = <<TXT; 
{ 

    "geolocation": { 

    "city": "Random City", 

    "region": "State", 

    "country": "US" 
    }, 
    "url": "https://www4.website.com/register/1234567", 

    "timestamp": "1237854980723.0239847" 
} 
TXT 

my @matches = /\s*\s*("[^"]+")\s*\s*:\s*("[^"]+")/gmx; 
my %hash = @matches; 

say join(",", keys %hash); 
say join(",", values %hash);   

Какой выход этого:

"city","country","region","timestamp","url" 
"Random City","US","State","1237854980723.0239847","https://www4.website.com/register/1234567" 

Конечно, если вы хотите использовать STDIN вместо замены определение строки с:

local $/ = undef; 
$_ = <>; 

Если вам нужен более надежный код, я предлагаю сначала совместить блок данных d в фигурные скобки. Затем вы будете искать ключ: значения.

Я хотел бы написать этот program.pl файл:

#!/usr/bin/env perl 
use v5.10; 
use Data::Dumper; 

local $/ = undef;  
open FILE, $ARGV[0] or die $!; 
$_ = <FILE>; 
close FILE; 

# Match all group { ... } 
my @groups = /((?&BRACKETED)) 
(?(DEFINE) 
    (?<WORD>  [^\{\}]+) 
    (?<BRACKETED> \s* \{ (?&TEXT)? \s* \}) 
    (?<TEXT>  (?: (?&WORD) | (?&BRACKETED))+) 
)/gmx; 

# Match any key:value pairs inside each group 
my @results; 
for(grep($_,@groups)) { 
    push @results, {/\s*\s*"([^"]+)"\s*\s*:\s*("[^"]+")/gmx}; 
} 

# For each result, we print the keys we want 
for(@results) { 
    say join ",", @$_{qw/city region country url timestamp/}; 
} 

Затем один пакетный файл для вызова скрипта:

rem How to call it... 
@perl program.pl text.txt > report.txt 
+0

Мне нравится ваш ответ. Он работал так, как я хотел, но просмотрите редактирование, которое я только что сделал по моему вопросу. Посмотрите на желаемый результат и отредактированные данные выборок, которые я предоставил. Что делать, если было 2 набора данных? Таким образом, csv будет содержать заголовок, который мы извлекли, а затем под ним будут строка данных 1, строка данных 2 и т. Д. @coin – JDE876

+0

@ JDE876 вторая версия скрипта выведет то, что вы ожидаете: две строки для каждого города. Но вместо того, чтобы использовать регулярное выражение для анализа ваших данных, я бы предложил использовать парсер JSON. – nowox

+0

есть ли какой-либо возможный способ, чтобы вы могли привести пример подстановки регулярного выражения парсером JSON? @coin – JDE876

0

Не издеваться над @ монеты регулярное выражение-фу, но преимущества использования модулей CPAN включают в себя получение более гибкого решения, которое вы можете использовать в будущем, и использование обработки кромочных дел, которые другие люди уже разработали.

Это решение использует модуль JSON для анализа ваших входящих данных (я предполагаю, что он по-прежнему выглядит как JSON), и модуль CSV для создания высококачественного CSV, который заботится о таких вещах, как встроенные кавычки и запятые внутри ваши данные.

use warnings; 
use strict; 

use JSON qw/decode_json/; 
use Text::CSV_XS; 

my $json_data_as_string = <<EOL; 
{ 
    "geolocation": { 
     "city": "Random City", 
     "region": "State", 
     "country": "US" 
    }, 
    "url": "https://www4.website.com/register/1234567", 
    "timestamp": "1237854980723.0239847" 
} 
EOL 

my $s = decode_json($json_data_as_string); 

my $csv = Text::CSV_XS->new({ binary => 1 }); 

$csv->combine(
    $s->{geolocation}{city}, 
    $s->{geolocation}{region}, 
    $s->{geolocation}{country}, 
    $s->{url}, 
    $s->{timestamp}, 
) || die $csv->error_diag;; 

print $csv->string, "\n"; 

Для чтения данных из файла в переменную $ json_data_as_string, вы можете использовать код из раствора @ монеты.

 Смежные вопросы

  • Нет связанных вопросов^_^