Zeile aus mehreren Dateien in eine neue Datei kopieren

Wenn du dir nicht sicher bist, in welchem der anderen Foren du die Frage stellen sollst, dann bist du hier im Forum für allgemeine Fragen sicher richtig.
sia
User
Beiträge: 11
Registriert: Freitag 31. Januar 2014, 14:05

Hallo Leute!

Ich bin neu hier im Forum und habe mich zunächst einmal etwas in den bereits existierenden Themen umgeschaut; bin aber leider nicht fündig geworden!
Ich arbeite noch nicht so lange mit Python und stehe relativ weit am Anfang. Daher komme ich bei folgendem Problem nicht weiter.

Ich habe mehrere Dateien.. Nennen wir sie mal D_1, D_2, D_3 usw. ich aus jeder dieser Dateien die achte Zeile in eine einzelene txt-Datei kopieren. Den umgekehrten Weg bin ich bereits geganegn und habe es geschafft aus einer Datei mehere Dateien zu erstellen. Doch irgendwie schaffe ich es nicht, aus mehreren Dateien eine bestimmte Zeile in eine neue Datei zu kopieren!

Lieben Gruß

Sia
Benutzeravatar
Hyperion
Moderator
Beiträge: 7478
Registriert: Freitag 4. August 2006, 14:56
Wohnort: Hamburg
Kontaktdaten:

sia hat geschrieben:Den umgekehrten Weg bin ich bereits geganegn und habe es geschafft aus einer Datei mehere Dateien zu erstellen. Doch irgendwie schaffe ich es nicht, aus mehreren Dateien eine bestimmte Zeile in eine neue Datei zu kopieren!
Sehr sonderbar, da Du für den einen Fall bereits alles an Handwerkszeug kennst, was Du für den anderen benötigst...

Evtl. zeigst Du uns mal Deine Versuche und wirst einmal ein wenig konkreter, was die Daten anbelangt. (Also zeig uns wirklich ein Beispiel)
encoding_kapiert = all(verstehen(lesen(info)) for info in (Leonidas Folien, Blog, Folien & Text inkl. Python3, utf-8 everywhere))
assert encoding_kapiert
123gro
User
Beiträge: 23
Registriert: Donnerstag 23. Januar 2014, 18:50

Vielleicht geht das wenn du die datei irgendwie in einen String umwandelst
und dann kann man ja einen String in "Scheiben" schneiden z.B.

wort="Hose"
wort[0:2]
Georg :)
BlackJack

Ungetestet:

Code: Alles auswählen

#!/usr/bin/env python
from itertools import islice


def main():
    with open('output.txt', 'w') as out_file:
        line_number = 7  # Counting starts at 0.
        for filename in ['d1.txt', 'd2.txt', 'd3.txt']:
            with open(filename, 'r') as lines:
                out_file.writelines(islice(lines, line_number, line_number + 1))


if __name__ == '__main__':
    main()
sia
User
Beiträge: 11
Registriert: Freitag 31. Januar 2014, 14:05

Hello..
Danke für Eure schnelle Antwort. Ich habe hier mal meinen Code, mit dem der besagte umgekehrte Weg begangen wurde:

Code: Alles auswählen

import numpy as np
import string
import os

s = np.genfromtxt('t_start.dat', unpack=True, dtype = int)
p = np.genfromtxt('t_stop.dat', unpack=True, dtype = int)
n = np.genfromtxt('number.dat', unpack=True, dtype = int)
for h in range(s.size):
	tmin = str(s[h])
	tmax = str(p[h])
	num = str(n[h])
	print tmin,tmax
	fobj_in = open("LC_Nebel_default.conf")
	fobj_out = open("LC_Nebel"+"_"+num,"w")	
	contents = fobj_in.readlines()
	contents.insert(35, '\ttag = LAT_SED_'+tmin+"_"+tmax)
	contents = "".join(contents)
	fobj_out.write(contents)
	for line in fobj_in:
	   fobj_out.write(line.rstrip()+"\n")
	fobj_out.write ('\ttmin = '+tmin +"\n")
	fobj_out.write ('\ttmax = '+tmax)
	fobj_out.close()
	fobj_in.close()
Also.. Die Datei LC_Nebel_default wird kopiert und der Inhalt wird in die Dateien LC_Nebel_1, LC_Nebel_2, LC_Nebel_3, ... usw. Kopiert. Ausserdem wird in die erstellten Dateien in Zeile 35 nach einer TAB-Taste "tag = LAT... eingefügt".. In jeder durchnummerierten LC_Nebel-Datei wird aus t_start und t_stop unten die entsprechenden Zeile hinzugefügt. :)
BlackJack

@sia: Ein paar Anmerkungen zu dem Quelltext:

Einrücktiefe ist per Konvention vier Leerzeichen pro Ebene.

Die Importierten Module `string` und `os` werden überhaupt nicht verwendet.

Einbuchstabige Namen sind in der Regel keine gute Idee. So ein Name soll dem Leser verraten was der Wert dahinter im Kontext des Programms bedeutet. Arrays `s`, `n`, und `p` zu nennen hilft dabei nicht. Und `h` ist auch ein ungewöhnlicher Name für eine Laufvariable die als Index verwendet wird.

Die Datei mit der immer gleichen Vorlage für jeden Schleifendurchlauf neu zu öffnen und einzulesen ist unnötige Arbeit. Die Daten kann man einmal *vor* der Schleife einlesen.

Zeile 15 liest die komplette Datei als Zeilen in eine Liste. Darum machen die Zeilen 19 und 20 überhaupt keinen Sinn weil damit nichts mehr aus der Datei gelesen werden kann. Wenn das ginge, wäre der `rstrip()`-Aufruf mit anschliessendem Ergänzen eines Zeilenendes wahrscheinlich auch nicht sehr sinnvoll.

An der Stelle sollte man auch überlegen ob man ``LC_Nebel_default.conf`` nicht als Template-Datei anlegt, zum Beispiel in dem man `string.Template` dafür verwendet, oder Platzhalter für einen `format()`-Aufruf in den Text der Datei einfügt.

Zeichenkettenformatierung sollte man auch dem Zusammensetzen von Zeichenketten und Werten mittels `str()` und ``+`` vorziehen, das ist übersichtlicher. Beim erstellen des Ausgabenamens ist das verketten von zwei literalen Zeichenketten mittels ``+`` unsinnig.

Ist das Absicht das die letzte Zeile der Ausgabedatei nicht mit einem Zeilenende-Zeichen abgeschlossen ist? Einige Werkzeuge können mit solchen Dateien Probleme bereiten.

Machen die `numpy`-Arrays überhaupt Sinn? Wenn ich das richtig sehe wird der Inhalt aus Textdateien, also Zeichenketten, in Zahlen umgewandelt, nur um die sofort wieder in Zeichenketten umzuwandeln.
sia
User
Beiträge: 11
Registriert: Freitag 31. Januar 2014, 14:05

@BlackJack:

Danke für deine Tipps..! Ich weiß, dass mein Quellcode ästhetisch nicht das ansprechendste ist. :) Doch ich musste unbeabsichtigt diesen Weg über Python gehen. Daher musste ich schnell eine Lösung finden, um meine Ausgangsdatei 500 mal zu kopieren und dabei zu achten, dass sie immer anders heißen und die letzten beiden Zeilen tmin und tmax enthalten. Es hat funktioniert..^^ Und dabei ist es beabsichtigt, dass am Ende kein Satzschlusszeichen oder so steht. Ebenso müssen die Dateien mit 1,2,3,.. usw. enden, weil das so vom Cluster verlangt wird!:)
BlackJack

Das hätte man auch ungefähr so schreiben können (ungetestet):

Code: Alles auswählen

#!/usr/bin/env python
from itertools import izip


def iter_stripped_lines(filename):
    with open(filename) as lines:
        for line in lines:
            yield line.strip()


def main():
    with open('LC_Nebel_default.conf') as template_file:
        template = template_file.read()

    filenames = ['number.dat', 't_start.dat', 't_stop.dat']
    for number, t_min, t_max in izip(*map(iter_stripped_lines, filenames)):
        print t_min, t_max
        with open('LC_Nebel_' + number, 'w') as out_file:
            out_file.write(template.format(t_min=t_min, t_max=t_max))


if __name__ == '__main__':
    main()
Warum Du das mit den Zahlen am Ende des Dateinamens erwähnst verstehe ich jetzt nicht. Es sei denn Du hast die Bemerkung zum Ausgabedateinamen falsch verstanden. Du hast dort nicht nur einen Wert drangepappt sondern auch *literale* Zeichenketten mit ``+`` zusammengesetzt, was keinen Sinn macht das unnötig kompliziert zu schreiben.
sia
User
Beiträge: 11
Registriert: Freitag 31. Januar 2014, 14:05

Ja, du hast recht.. das + muss vor dem num nicht hin :roll: . Ich war froh, dass die Ausgabedateien mit ..._1, ..._2 ..usw. enden. Das ist auch so gewollt, dass die Datei mit einer Zahl endet. :)
BlackJack

@sia: Irgendwie habe ich immer noch das Gefühl Du hast den Punkt nicht verstanden, denn vor dem `num` muss natürlich ein ``+`` stehen, sonst wäre das ein Syntaxfehler. Du hast geschrieben ``'LC_Nebel' + '_' + num`` und da ist das Zusammensetzen von 'LC_Nebel' und '_', statt es gleich zusammen zu schreiben, unsinnig hoch drei.
sia
User
Beiträge: 11
Registriert: Freitag 31. Januar 2014, 14:05

Achsoo.. Ja, klar..! :D Jetzt weiß ich, was du meinst.. Es ginge ja auch einfach 'LC_Nebel_' +num..! (Wie Du ja bereits vorgeschlagen hattest..!:))
sia
User
Beiträge: 11
Registriert: Freitag 31. Januar 2014, 14:05

Hallo BlackJack,

du hattest relativ weit am Anfang einen ungetesteten Weg gepostet. Bekomme dabei einen Syntax-Fehler:

with open ('output.txt', 'w') as out_file:
^
SyntaxError: invalid syntax

Ich weiss nicht, woran das liegen mag!

Lieben Gruss
Benutzeravatar
pillmuncher
User
Beiträge: 1484
Registriert: Samstag 21. März 2009, 22:59
Wohnort: Pfaffenwinkel

sia hat geschrieben: with open ('output.txt', 'w') as out_file:
^
SyntaxError: invalid syntax

Ich weiss nicht, woran das liegen mag!
Entweder, du hast in den Zeilen davor ein öffnende Klammer ohne die dazu gehörende schließende, oder du verwendest Python < 2.6, wo es das with-Statement noch nicht gab. Bei Python 2.5 kannst du es verwenden, sofern du in deinem Modul ganz oben (nach dem shebang) die Zeile

Code: Alles auswählen

from __future__ import with_statement
einfügst.
In specifications, Murphy's Law supersedes Ohm's.
sia
User
Beiträge: 11
Registriert: Freitag 31. Januar 2014, 14:05

@Pillmuncher

Python-Version ist 2.4.3 :shock:
Gibt es dafuer auch eine Loesung? :) (..ausser Update. Kommt leider nicht in Frage)
BlackJack

@sia: Eine neuere installieren. :-) Python 2.5 ist mittlerweile 8 Jahre alt.
sia
User
Beiträge: 11
Registriert: Freitag 31. Januar 2014, 14:05

@BlackJack:
Ich weiss, dass ein Update ueberfaellig ist :D Ich sitze aber an einem Rechner der Uni und da kann ich nicht sehr viel an der Software aendern. Zu Hause sollte es dann klappen :)
EyDu
User
Beiträge: 4881
Registriert: Donnerstag 20. Juli 2006, 23:06
Wohnort: Berlin

Du könntest dort eine aktuelle Version in deinem Benutzerverzeichnis kompilieren.
Das Leben ist wie ein Tennisball.
sia
User
Beiträge: 11
Registriert: Freitag 31. Januar 2014, 14:05

@EyDu: Danke.


@Alle: Das Problem ist jetzt mit einem Shell-Skript geloest worden! :D
sia
User
Beiträge: 11
Registriert: Freitag 31. Januar 2014, 14:05

Falls es jemanden noch interessieren koennte:

Es gibt da soetwas schoenes wie 'grep' habe ich als shell-skript laufen lassen

Code: Alles auswählen

for file in `ls *results`
do
   tmin=`grep tmin $file | awk '{print $2}'`
   tmax=`grep tmax $file | awk '{print $2}'`
   Integral=`grep \^Integral $file | awk '{print $2}'`
   dIntegral=`grep -P 'dIntegral\t'  $file | awk '{print $2}'`
   t=`echo \($tmin+$tmax\)/2 | bc`
   echo $t $Integral $dIntegral  
 
done
Damit durchsucht man alle Dateien, die mit "results" enden nach tmin (grep tmin $file | awk '{print $2}') wobei in der Originaldatei tmin = 123456 steht. mit awk nimmt man sich 123456 heraus. Anschliessend kann man mit > outputfile.txt alle in eine Datei reinschreiben.

Liebe Gruesse

Sia
Zuletzt geändert von Anonymous am Freitag 7. Februar 2014, 18:46, insgesamt 1-mal geändert.
Grund: Quelltext in Bash-Code-Tags gesetzt.
BlackJack

@sia: Kleine Maneuverkritik:

Die Backticks ` sind nicht besonders gut lesbar und verschachteln kann man sie auch nicht. Stattdessen würde ich die äquivalente $(…)-Syntax verwenden. Weiterer Pluspunkt: man kann ``$(…)`` direkt in "-Zeichenketten verwenden.

Über ``$(ls pattern)`` zu iterieren ist falsch. Sowie in den Dateinamen irgendwas ”komisches” vorkommt fällt man damit auf die Nase. Korrekt wäre es ganz einfach nur das Muster zu verwenden, also in Deinem Fall ``for file in *results``.

Ebenfalls wegen möglicher Sonderzeichen, da fallen auch schon ansonsten harmlose Leerzeichen drunter, sollte man immer wenn man ``$file`` verwendet doppelte Anführungszeichen setzen, damit das auch ganz sicher nur *ein* Argument ist.

Wenn bei ``bc`` die Nachkommestallen gar nicht benutzt werden und das ganze am ende in einer Bash läuft, kann man `$t` auch die Shell ausrechnen lassen. Da kommt bei mir am Ende das hier heraus (ungetestet):

Code: Alles auswählen

#!/usr/bin/bash

for file in *results
do
    tmin=$(grep 'tmin' "$file" | awk '{print $2}')
    tmax=$(grep 'tmax' "$file" | awk '{print $2}')
    Integral=$(grep '\^Integral' "$file" | awk '{print $2}')
    dIntegral=$(grep -P 'dIntegral\t' "$file" | awk '{print $2}')
    t=$(echo "($tmin + $tmax) / 2" | bc)
    # alternativ in Bash:
    # t=$((($tmin + $tmax) / 2))
    echo "$t $Integral $dIntegral"
done
Was hier aber immer noch nahezu ein Verbrechen ist: Jede Datei wird *vier* mal komplett von Anfang bis Ende nach jeweils einem Wert durchsucht und jedes mal wird ``awk`` auf eine Zeile angewendet, nur um das Feld aus der Zeile zu extrahieren. Wenn man sowieso schon AWK als Abhängigkeit hat, kann man *das* auch gleich verwenden um das gesamte Problem zu lösen. Genau für solche Aufgaben ist es ja gedacht. Ungetestet:

Code: Alles auswählen

#!/usr/bin/env awk

/tmin/ { tmin = $2 }
/tmax/ { tmax = $2 }
/^Integral/ { integral = $2 }
/dIntegral\t/ { d_integral = $2 }

END { print (tmin + tmax) / 2, integral, d_integral }
Ich habe einfach mal die regulären Ausdrücke aus dem Shell-Skript übernommen, da würde man hier vielleicht etwas spezifischere Tests auf Feldwerte machen wollen.
Antworten