monidfix.pl: IDs der Monatsnamen korrigieren


Der Fehler

Die Kennungen der sprachabhängigen Texte in den Dateien DOCROOT/local/local.xml.LANG. sind geändert. Zu den sprachabhängigen Texten gehören die Monatsnamen. In den Kalendergrunddaten-Dateien DOCROOT/kal/b/YEAR/COUNTRY.xml. und DOCROOT/kal/b/YEAR/a.xml.LANG. stehen aber noch die alten Kennungen. Sie müssen durch die neuen Kennungen ersetzt werden. Wegen dieses Fehlers fehlen die Monatsnamen in den Kalendern.

Dazu benutze ich ein Perl-Skript.

Die alten Kennungen

Die alten Kennungen werden in jeder Grunddaten-Datei benutzt. Ich lese sie aus der Datei DOCROOT/kal/b/2020/de.xml.. Die Platzhalter sind in der Form <l:ph id="ID"/> in der Datei enthalten. Die Datei enthält die Kalenderdaten nicht nur für das aktuelle Jahr, sondern auch für den Dezember des Vorjahres und den Januar des Folgejahres. Die Daten für jeden Monat enthalten den Platzhalter für den Monatsnamen meist mehrfach. Der folgende Perl-Code liest die alten Kennungen.

   my $oldids = [];
   my $fn   = "DOCROOT/kal/b/2020/de.xml.";
   my $verb = 1;
   my $h;
   if (!open ($h, "<", $fn)) {
      print STDERR "Kann Datei \"$fn\" nicht öffnen\n";
      return;
   }
   my $d;
   {
      local $INPUT_RECORD_SEPARATOR;
      $d = <$h>;
      close $h;
   }
   my $pid = "";
   my $id  = "";
   while ( $d =~ /<l:ph id="([a-z0-9]+)"\/>/g ) {
      $id = $1;
      if ($id ne $pid) {
         push (@$oldids, $id);
         $pid = $id;
      }
   }
   if ($verb) {
      $pid = 0;
      for $id (@$oldids) {
         print "$pid $id\n";
         ++$pid;
      }
   }
  

Die neuen Kennungen

Die Texte zu den Kennungen lese ich aus der Datei DOCROOT/local/local.xml.de. mit den deutschen Texten. Sie sind in der Form <t id="ID">TEXT</t> enthalten. Ich speichere die Kennungen zu allen Texten als $newids -> {"TEXT"} = "ID".

   my $newids = {};
   $fn = "DOCROOT/local/local.xml.de.";
   $h  = undef;
   if (!open ($h, "<:encoding(utf-8)", $fn)) {
      print STDERR "Kann Datei \"$fn\" nicht öffnen\n";
      return;
   }
   {
      local $INPUT_RECORD_SEPARATOR;
      $d = <$h>;
      close $h;
   }
   # <t id="cr">Januar</t>
   while ( $d =~ /<t id="([a-z0-9]+)">([^>]+)<\/t>/g ) {
      $newids -> {$2} = $1;
   }
   my $names = [
      "Januar",
      "Februar",
      "März",
      "April",
      "Mai",
      "Juni",
      "Juli",
      "August",
      "September",
      "Oktober",
      "November",
      "Dezember",
   ],
   if ($verb) {
      my $nm;
      my $i = 0;
      for $nm (@$names) {
         ++$i;
         print "$i $nm ", $newids -> {$nm}, "\n";
      }
   }
  

Alte und neue Kennungen zuordnen

Nun ordne ich die neuen Kennungen den alten Kennungen zu in der Form $idmap -> {"OLD_ID"} = "NEW_ID"}.

Der erste Eintrag in der Liste der alten Kennungen ist die Kennung für den Namen des Monats Dezember des Vorjahres. Ich ignoriere diesen Eintrag. Die nächsten zwölf Listeneinträge sind die Kennungen der Namen der Monate von Januar bis Dezember. Die Zuordnung ist einfach:

   my $idmap = {};
   shift @$oldids;
   my $nm;
   while (@$names) {
      $nm = shift @$names;
      $id = shift @$oldids;
      $idmap -> {$id} = $newids -> {$nm};
   }
  

Alte Kennungen ersetzen

Ich erstelle jetzt eine Funktion, die in einer Kalenderdaten-Datei die alten Kennungen durch die neuen Kennungen ersetzt. Ein regulärer Ausdruck findet alle Platzhalter der Form <l:ph id="ID"/>. Die alte ID ist die erste Match-Gruppe $1 des regulären Ausdrucks. Eine Hilfsfunktion liefert den Platzhalter mit der neuen ID: <l:ph id="NEWID"/>. Eine alte Kennung, der nicht eine neue Kennung zugeordnet ist (also kein Monatsname), bleibt stehen.

   my $subst = sub {
      $id = $idmap -> {$1} || $1;
      return "<l:ph id=\"$id\"/>";
   };
  

Die Funktion zur Verarbeitung einer Datei nimmt zwei Parameter: $in ist der Dateipfad der Eingabedatei mit den alten Kennungen, $out ist der Dateipfad der Ausgabedatei mit den neuen Kennungen.

   my $proc_file = sub {
      my ($in, $out) = @_;
      print "$in -> $out\n" if $verb;
      $h = undef;
      if (!open ($h, "<:encoding(utf-8)", $in)) {
         print STDERR "Kann Datei \"$in\" nicht öffnen\n";
         return;
      }
      {
         local $INPUT_RECORD_SEPARATOR;
         $d = <$h>;
         close $h;
      }
      $d =~ s/<l:ph id="([a-z0-9]+)"\/>/$subst -> ($idmap, $1)/ge ;
      $h = undef;
      if (!open ($h, ">:encoding(utf-8)", $out)) {
         print STDERR "Kann Ausgabedatei \"$out\" nicht öffnen\n";
         return;
      }
      print $h $d;
      close $h;
   };
  

Alle Dateien verarbeiten

Alle Grunddaten-Dateien DOCROOT/kal/b/YEAR/COUNTRY.xml. und DOCROOT/kal/b/YEAR/a.xml.LANG. sind zu korrigieren. Für die korrigierten Dateien wähle ich statt DOCROOT/kal/b ein anderes Pfad-Präfix (DOCROOT/kal/bnew. Ich lese das Verzeichnis DOCROOT/kal/b und die Unterverzeichnisse YEAR und verarbeite die Grunddaten-Dateien:

   use File::Spec::Functions qw(catdir catfile);
   use File::Path qw(make_path);

   my $indir  = "DOCROOT/kal/b";
   my $outdir = "DOCROOT/kal/bnew";
   my ($dh, $sdh);
   my ($de, $sde);
   my $dp;
   my $od;
   opendir ($dh, $indir);
   while (defined ($de = readdir ($dh))) {
      next unless $de =~ /^20\d\d$/;
      $dp = catdir ($indir, $de);
      next unless -d $dp;
      $sdh = undef;
      opendir ($sdh, $dp)
      $od = catdir ($outdir, $de);
      make_path ($od);
      while (defined ($sde = readdir ($sdh))) {
         next unless $sde =~ /\.xml(?:\..+)?\.$/;
         $proc_file -> (catfile ($dp, $sde), catfile ($od, $sde));
      }
      closedir $sdh;
   }
   closedir $dh;
  

Gzip-komprimierte Dateien

Zu jeder Grunddaten-Datei füge ich eine gzip-komprimierte Datei hinzu mit dem Suffix .gz. Dazu benutze ich ein bash-Skript:

#!/bin/bash
b=$(realpath $0);
b=${b%/src/*};
dir=$b/docroot/kal/bnew;
for sd in $dir/*; do
   echo "subdir $sd";
   f=${sd#$dir/};
   [[ $f =~ ^20[0-9][0-9]$ ]] || continue;
   [[ -d $sd ]] || continue;
   for f in $sd/*xml*\. ; do
      echo $f;
      gzip --best --stdout $f > ${f}gz ;
   done;
done;
  

Die Skripte

Die vollständigen Skripte sind monidfix.pl (Quelltext) und monidfix_addgz.