2016-04-13 24 views
1

Ich habe etwa tausend XML-Dateien in einem Ordner. Jede XML-Datei enthält ungefähr 100 Elemente. Jeder Artikel befindet sich in einer separaten Zeile.Suchen und Ersetzen von spezifischem Text zwischen zwei Strings?

Ich brauche Text zu suchen und zu ersetzen, die nur zwischen

<content:encoded><![CDATA[ 

und

]]></content:encoded> 

ist nur ich brauche die folgenden ersetzen:

  • ' wird ersetzt mit &apos;
  • " mit &quot;
  • < ersetzt wird ersetzt durch &lt;
  • > mit &gt;

I sed für die Massenfeststellung/ersetzen die ganze Zeit ersetzt, kann es aber, wenn ich zur Arbeit nicht nur erhalten zwischen Strings denken suchen/ersetzen wollen wie diese

ich bin oben für die Verwendung von was auch immer euch am besten

+0

Könnte XSLT helfen? – WBT

+0

Ist es gültiges XML oder versuchen Sie, es gültig zu machen? –

+0

Nun der ungültige Teil ist die Tatsache, dass es Unescaped Zeichen wie '' 'drin gibt, deshalb versuche ich, sie zu ersetzen. – Edward

Antwort

0

Sobald das "Start-Token" gefunden wird, muss die Lösung alles (außer dem "Ende-Token") sammeln (zusammenpassen) - aber die Suche nach der Negation einer Zeichenkette ist überraschend schwierig. (für einige Diskussion siehe here und here).

Unten finden Sie eine Lösung unter ein paar leidlich (glaube ich) Zwänge

  1. Die Token nicht heißt [start] stuff [start] stuff [end] stuff [end] verschachtelt werden können; und
  2. Weder die Start- oder End-Token können aufgeteilt werden über Leitungen dh

    hello world <content:enco

    ded><![CDATA[ [stuff] ... etc

Meine Lösung ist lang, aber reichlich kommentiert und ist mehr geradlinig und nicht klug (wohl) leichter zu pflegen;

use v5.12; 

my $start_string = '<content:encoded><![CDATA[' ; 
my $end_string = ']]></content:encoded>' ; 
my $start_token = quotemeta $start_string ; 
my $end_token = quotemeta $end_string ; 

sub do_subs { 
    my $text = shift ; 
    $text =~ s/'/\&apos;/g ; 
    $text =~ s/"/\&quot;/g ; 
    $text =~ s/\</\&lt;/g ; 
    $text =~ s/\>/\&gt;/g ; 
    return $text ; 
} 

my $subs_mode = 0;    # "substitution mode" off/on 
while (<>) { 
    my $line_remnants = $_ ;  # what's left - intially, the whole line 
    my $replacement = "" ;  # replacement for whole line 

    # while there's something left of the line to process 
    while (! $line_remnants eq "") { 
     if ($subs_mode) { 
      # Currently substituting. Scan for end_token 
      if ($line_remnants =~ /^ (.*?) $end_token (.* \n) /x) { 
       # Found end_token -> &do_subs on "preface" & add end_string 
       $replacement .= do_subs($1) . $end_string ; 
       $line_remnants = $2 ; 
       $subs_mode = 0 ; 
      } 
      else { 
       # Didn't find end_token -> &do_subs on all of what's left 
       $replacement .= do_subs($line_remnants) ; 
       $line_remnants = "" ; 
      } 
     } 
     else { 
      # Currently NOT substituting. Scan for start_token 
      if ($line_remnants =~ /^ (.*?) $start_token (.* \n) /x) { 
       # Found start_token -> append "preface" and start_string 
       $replacement .= $1 . $start_string ; 
       $line_remnants = $2 ; 
       $subs_mode = 1 ; 
      } 
      else { 
       # Didn't find start_token -> append all of what remains 
       $replacement .= $line_remnants ; 
       $line_remnants = "" ; 
      } 
     } 
    } # while ! $line_remnants ... 

    # Nothing left of line, print replacement 
    print $replacement 
} 

Es ist 'Unix-Filter' Stil - liest auf STDIN, transformiert und schreibt auf STDOUT. Wenn das gefüttert wird;

hello world 
<content:encoded><![CDATA[ ' " ]]></content:encoded> 
Here it comes: <content:encoded><![CDATA[ No quotes 
like these in here ' " or relation ops like these < > ",>' 
More non-allowed " ' <>'" - then the end: ]]></content:encoded> 
these qotes should come through ' "<> 
Start and End on one line - no data 
<content:encoded><![CDATA[]]></content:encoded> 
Start and End repeatedly on one line - single char 
'<content:encoded><![CDATA[']]></content:encoded>'<content:encoded><![CDATA[']]></content:encoded> 

... es produziert;

hello world 
<content:encoded><![CDATA[ &apos; &quot; ]]></content:encoded> 
Here it comes: <content:encoded><![CDATA[ No quotes 
like these in here &apos; &quot; or relation ops like these &lt; &gt; &quot;,&gt;&apos; 
More non-allowed &quot; &apos; &lt;&gt;&apos;&quot; - then the end: ]]></content:encoded> 
these qotes should come through ' "<> 
Start and End on one line - no data 
<content:encoded><![CDATA[]]></content:encoded> 
Start and End repeatedly on one line - single char 
'<content:encoded><![CDATA[&apos;]]></content:encoded>'<content:encoded><![CDATA[&apos;]]></content:encoded> 

Ich hoffe, dass es von einigen Nutzen ist.

Verwandte Themen