Come posso usare awk o Perl per incrementare un numero in un file XML di grandi dimensioni?

StackOverflow https://stackoverflow.com/questions/448284

  •  19-08-2019
  •  | 
  •  

Domanda

Ho un file XML con la seguente riga:

            <VALUE DECIMAL_VALUE="0.2725" UNIT_TYPE="percent"/>

Vorrei incrementare questo valore di .04 e mantenere il formato dell'XML in atto. So che questo è possibile con uno script Perl o awk, ma ho difficoltà con le espressioni a isolare il numero.

È stato utile?

Soluzione

Se sei su una scatola con il comando xsltproc in posizione, ti suggerirei di usare XSLT per questo.

Per una soluzione Perl sceglierei il DOM. Consulta questo Elaborazione DOM con Perl in uscita.

Detto questo. Se il tuo file XML viene prodotto in modo prevedibile, potrebbe funzionare qualcosa di simile a quanto segue:

perl -pe 's#(<VALUE DECIMAL_VALUE=")([0-9.]+)(" UNIT_TYPE="percent"/>)#"$1" . ($2 + 0.4) . "$3"#e;'

Altri suggerimenti

Se sei assolutamente sicuro che il formato del tuo XML non cambierà mai, che l'ordine degli attributi è fisso, che puoi effettivamente ottenere la regexp per il numero giusto ... allora scegli la soluzione non parser .

Personalmente userei XML :: Twig (forse perché l'ho scritto; -). Elaborerà l'XML come XML, pur rispettando il formato originale del file e non caricherà tutto in memoria prima di iniziare a lavorare.

Codice non testato di seguito:

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

use XML::Twig;

XML::Twig->new( # call the sub for each VALUE element with a DECIMAL_VALUE attribute
                twig_roots => { 'VALUE[@DECIMAL_VALUE]' => \&upd_decimal },
                # print anything else as is
                twig_print_outside_roots => 1,
              )
         ->parsefile_inplace( 'foo.xml');

sub upd_decimal
  { my( $twig, $value)= @_; # twig is the XML::Twig object, $value the element
    my $decimal_value= $value->att( 'DECIMAL_VALUE');
    $decimal_value += 0.4;
    $value->set_att( DECIMAL_VALUE => $decimal_value);
    $value->print;
  }

Questo prende input su stdin, output su stdout:

while(<>){
 if( 

Questo prende input su stdin, output su stdout:

<*> =~ /^(.*DECIMAL_VALUE=\")(.*)(\".*)$/ ){ $newVal = $2 + 0.04; print "$1$newVal$3\n"; }else{ print

Questo prende input su stdin, output su stdout:

<*>; } }

Qualcosa di simile al seguente funzionerà. Potrebbe essere necessario modificare se c'è spazio extra, ma questo è lasciato come esercizio per il lettore.

function update_after(in_string, locate_string, delta) {
    local_pos = index(in_string,locate_string);
    leadin    = substr(in_string,0,local_pos-1);
    leadout   = substr(in_string,local_pos+length(locate_string));
    new_value = leadout+delta;
    quote_pos = index(leadout,"\"");
    leadout   = substr(leadout, quote_pos + 1);
    return leadin locate_string new_value"\"" leadout;
}

/^ *\<VALUE/{
    print  update_after(<*>, "DECIMAL_VALUE=\"",0.4);
}

ecco gawk

awk '/DECIMAL_VALUE/{
 for(i=1;i<=NF;i++){
    if( $i~/DECIMAL_VALUE/){
        gsub(/DECIMAL_VALUE=|\042/,"",$i)
        $i="DECIMAL_VALUE=\042"$i+0.4"\042"
    }
 }
}1' file
Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top