Trick 57: Sauberes Filtern   (Ohne unfreiwillige Umcodierung)

Aufgabe: Schnell mal eben eine Textdatei einlesen und wieder

            rausschreiben, garantiert ohne daß ein Zeichen verändert
            wird, außer bestimmten Zeichen(folgen) oder Zeilen.

Warum: Mitunter liegt eine Textdatei vor (ASCII oder ANSI oder noch was
       anderes) und man muß einzelne Zeilen hinauswerfen, herausfischen
       oder in ganz bestimmter Weise verändern, alles andere soll aber
       exakt so bleiben, wie es ist. Solche Vorgänge werden gern als
       "Filtern" bezeichnet.
       Perlentaucher und Pythonbändiger werden hier abwinken: Mach'
       ich mit einem Zweizeiler, wenn nicht gar mit "grep"! Sicher,
       aber es geht darum, solche Vorgänge auch bei Bedarf innerhalb
       eines FLEXes nebenbei miterledigen zu können, als Teil einer
       größeren Aufgabe. Zwar kann ein FLEX auch Python- oder Perl-
       skripte anstoßen und deren Ergebnisse verwerten, aber das
       Vorhandensein der betr. Skriptsprache kann man leider nicht
       generell voraussetzen.
       OK, es gibt schon den FLEX file.flx, der Textdateien zeilenweise
       liest und wieder schreibt. Aber dabei verschwinden Leerzeichen
       am Zeilenende. Und: Sonderzeichen außerhalb des definierten
       Bereichs von OstWest-Zeichen, vor allem in der Gegend von
       186-218, werden u.U. verfälscht. Das soll nicht sein!

Lösung: Die Zeilen werden eingelesen wie sonst auch, sie werden aber
        NICHT in einer #u-Variablen zwischengespeichert, sondern, und
        das ist der Trick, ganz ohne solche Umschweife gleich wieder
        rausgeschrieben.
        Der Hinweis soll nicht fehlen:
        WENN Zwischenspeicherung, dann in einer $-Variablen! Diese
        unterliegen beim Einlesen und Speichern keiner Umcodierung
        oder Leerzeichenbeseitigung (siehe Fußnote ganz unten).

Sagen wir, unsere Ausgangsdatei ist "input.txt", die Ergebnisdatei soll
"output.txt" heißen.

Wir zeigen hier einen FLEX-Abschnitt, der das Problem löst, auf die
kürzestmögliche Weise. Diesen Abschnitt kann man sich herauskopieren
und in beliebige FLEXe modifiziert einbauen, wo man derartige Aktionen
braucht. Es sind mehr als zwei Zeilen, zugegeben, es sind sieben (ohne
Öffnen und Schließen), aber kurz und unkryptisch.

  ------- BEGINN DES MUSTERS --------------------------------------
...
  Die Dateien öffnen (zum Lesen bzw. Schreiben)
open input.txt
open x output.txt
  ^^^^^^^^^^^^^^^^^ Beginn der Schleife
:GLOOP
  naechste Zeile lesen
get iV
  war keine Zeile mehr da? Dann Ende
if cancel jump GLEND

  eine gelesene Zeile steht jetzt in der iV
  ***************************************************
  Hier ist der Platz zum Manipulieren der Zeile!
  Ein paar Beispiele:
     Hinauswerfen: wenn "xyz" drinsteht, die Zeile weglassen
  if %xyz% jump GLOOP
     Herausfischen: wenn "xyz" drinsteht, die Zeile übernehmen
  if not %xyz% jump GLOOP
     Verändern: Z.B. Text-Ersetzung 'abc' durch 'xyz':
  ins _abc_xyz_
  ***************************************************
  In iV steht jetzt ein u.U. veränderter Inhalt
  Raus damit in die Datei, dann Zeilenvorschub dahinter:
write
write n
jump GLOOP

:GLEND
  ^^^^^^^^^^ Ende der Schleife
  Dateien schliessen
close
close x
...

  ------- ENDE DES MUSTERS -------------------------------------
 
Hinweis:
Man KANN auch auf Trick 44 zurückgreifen, der das Umcodieren einer
Datei vorführt! Man nimmt schlicht die Zeilen heraus, die sich mit
der Umcodierung befassen, bes. die Zeile  xcode xp. Fertig eingebaut
ist das in den mitgelieferten FLEX  filecode.flx.
Ferner kann man kombinieren mit Trick 52 ("Dateien abklappern"), um
mehrere Dateien in gleicher Weise zu filtern.

ABER ACHTUNG:
Hier ist von TEXTdateien die Rede, die aus ZEILEN bestehen! Die
Zeilen sind durch die Codes 13 10 getrennt, der Befehl "get"
liest genau die Zeichen zwischen zwei solchen Kombinationen,
die Steuerzeichen selbst gehören nicht zur Zeile. Sie werden deshalb
im write-Befehl mit dem  n  (für 'newline') wieder angefügt.
Dateien ohne solche Struktur sind ein anderes Thema (Trick 58).

Fußnote zur Umcodierung
=======================
Wenn man im FLEX schreibt:
var "xyz"\ins #nnn
was passiert dann?
Dann wird der Text, der in der iV steht, so behandelt, als hätte man
ihn gerade im Schreibfeld eingegeben. Und was ist das Problem?
Was man im Schreibfeld eingibt, das ist intern immer ANSI-Code.
Im FLEX kann es aber sein, wenn er z.B. mit X-Editor erstellt
wurde oder wenn man ASCII-Daten bearbeitet, daß der iV-Inhalt nicht
ANSI ist, sondern ASCII. Was per Schreibfeld eingegeben wurde,
das wird per o-Tabelle der Indexparameter in den internen Daten-
bankcode gewandelt, normalerweise also von ANSI nach ASCII.
Deshalb wird im Normalfall unser ASCII-"xyz" VOR der Übergabe an
die Verarbeitung noch per o-Tabelle nach ANSI gewandelt - um dann
sofort wieder zurückverwandelt zu werden. Na schön, dann ist es
wieder mit "xyz" identisch, alles in Butter!? Normalerweise ja, aber
beim Vararbeiten einer Textdatei kann diese auch Zeichen enthalten,
die es im OstWest-ASCII nicht gibt, z.B. 202 oder 217. Diese werden
per o.apt - aus lauter Verlegenheit - alle auf 127 abgebildet. Für
die Abbildung ANSI->ASCII bedeutet das aber, daß sie alle auf 217
landen, welches zufällig der letzte Wert in o.apt ist, der die 127
zugewiesen bekommt. [Könnte man das nicht wirklich nochmal ändern?
Gute Frage, wird notiert. Könnte nicht mit "set c2" die gesamte
Umschalterei abgeschaltet werden? Guter Vorschlag, steht schon
auf dem Zettel für V28.]

Wenn der FLEX nun aber mit einem ANSI-Editor (z.B. NotePad) erstellt
wurde? Dann muß man
set c1
geben, bevor ein "insert" kommt. Die erste der beiden Umwandlungen
(die des FLEX-Textes in ANSI) unterbleibt dann, aber die zweite muß
natürlich sein.

Aber wenn man intern ANSI hat und nicht ASCII? Dann braucht man eine
andere o.xpt! Wie z.B. beim N-Format: Die o.npt ist fast leer,
nur wenige Werte müssen aus internen Gründen umgewandelt werden.

Klingt verzwickt? Klar, müßte alles nicht sein, wenn man nur ein
einziges Codesystem hätte, z.B. UTF-8. So bequem ist die reale Welt
aber nun mal nicht beschaffen.