Wie (Uhr/Tages) Zeiten zusammen zählen ?

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.
Antworten
Michaela81
User
Beiträge: 18
Registriert: Samstag 5. September 2020, 18:47

Hallo, ich schaffe es leider nicht mehrere Zeitangaben zusammen zu zählen.
Und warum funktioniert folgendes Konstrukt zwar zum abziehen einer Zeit, nicht aber wenn man sie so mit einem Pluszeichen zusammenzählen will ?

Code: Alles auswählen

from datetime import datetime
s1 = '10:04:20'
s2 = '00:04:10'
format = "%H:%M:%S"

timea = (datetime.strptime(s1,format)) - (datetime.strptime(s2,format))
print(re.sub(".* ","",str(timea)))
Hintergrund: Ich habe mir mit Mediainfo die Spieldauern einzelner Lieder in eine Datei ausgeben lassen.
Diese möchte ich zu einer Gesammtspielzeit zusammenrechnen. Dankt datetime.strptime() habe ich jetzt die Spieldauer aller Songs im Format %H:%M:%S vorliegen, schaffe es aber nicht eine Lösung zu finden diese zusammen zu zählen ohne erst alles in Sekunden zu wandeln.


EDIT:
Der vollständigkeit halber:
Gegeben ist beispielsweise eine Datei 'www.media' mit dem Inhalt

Code: Alles auswählen

Trackname..: 01- Song - Never Say Die 
     Info.......: MPEG Audio 160 kb/s @ CBR  ##  Laenge: 4 min 39 s  ##  Groesse: 5.34 MiB
Trackname..: 02- Song - Resurrector 
     Info.......: MPEG Audio 160 kb/s @ CBR  ##  Laenge: 4 min 24 s  ##  Groesse: 5.05 MiB
Trackname..: 03- Song - The Demise 
     Info.......: MPEG Audio 160 kb/s @ CBR  ##  Laenge: 4 min 0 s  ##  Groesse: 4.59 MiB
Trackname..: 04- Song - Clockwork Mary 
     Info.......: MPEG Audio 160 kb/s @ CBR  ##  Laenge: 4 min 19 s  ##  Groesse: 4.95 MiB
Trackname..: 05- Song - Tear Down The Walls 
     Info.......: MPEG Audio 160 kb/s @ CBR  ##  Laenge: 3 min 39 s  ##  Groesse: 4.19 MiB
Trackname..: 06- Song - Come Back To Black 
     Info.......: MPEG Audio 160 kb/s @ CBR  ##  Laenge: 4 min 49 s  ##  Groesse: 5.51 MiB
Trackname..: 07- Song - All My Life 
     Info.......: MPEG Audio 160 kb/s @ CBR  ##  Laenge: 2 min 34 s  ##  Groesse: 2.96 MiB
Trackname..: 08- Song - Destiny_s To Come 
     Info.......: MPEG Audio 160 kb/s @ CBR  ##  Laenge: 4 min 34 s  ##  Groesse: 5.24 MiB
Trackname..: 09- Song - The Last Redemption 
     Info.......: MPEG Audio 160 kb/s @ CBR  ##  Laenge: 13 min 39 s  ##  Groesse: 15.6 MiB
Trackname..: demo 
     Info.......: MPEG Audio 56.0 kb/s @ CBR  ##  Laenge: 5 s 330 ms  ##  Groesse: 38.0 KiB
Trackname..: thunder 
     Info.......: MPEG Audio 98.5 kb/s @ VBR  ##  Laenge: 25 s 103 ms  ##  Groesse: 303 KiB
Dise Datei bearbeite ich folgendermassen: Das ist sicher etwas umständlich, aber es funktioniert soweit. Und ist eben selbst erarbeitet.

Code: Alles auswählen

track_in = "F:\Python\www.media"       
if os.path.isfile(track_in):
    file = open(track_in)                             
    lines_in = file.readlines()                     
    file.close()                                                  
    
    # Vorlagendatei Zeilenweise bearbeiten    
    k = float()
    format1 = "Laenge: %M min %S s  ##"
    format2 = "Laenge: %S s %f ms  ##"
    format3 = "%H:%M:%S"
    for line_t in lines_in:        
        matches_2 = re.search("Laenge.*\#", line_t)  
        try:
            if re.search("min",matches_2.group()):
                try:
                    timea = datetime.strptime(matches_2.group(),format1)
                    timef = re.sub(".* ","",str(timea))                                        
                except:
                    pass
            if re.search("ms",matches_2.group()):
                try:
                    timea = datetime.strptime(matches_2.group(),format2)
                    timef = re.sub(".* ","",str(timea))
                    timef = re.sub("\..*","",timef )                         
                except:
                    pass
            print(timef)
        except:
            pass
Benutzeravatar
__blackjack__
User
Beiträge: 13110
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

@Michaela81: Weil man Datumsangaben nicht addieren kann. Das macht schlicht keinen Sinn. Und bitte nicht mit regulären Ausdrücken an der Zeichenkettendarstellung von `timedelta`-Objekten herumbasteln. Wobei das in diesem Fall auch überhaupt keinen Effekt hat. Und in dem Fall wo es etwas bewirken würde, würde viel Zeit einfach unter den Teppich gekehrt.

Ich würde da was eigenes programmieren statt `datetime` zu missbrauchen, denn Du willst mit Zeitdauern rechnen, `datetime` ist für Zeitpunkte. Das ist etwas anderes. Das einzige was da nützlich wäre ist `timedelta`, das hat aber nichts um das Ergebnis sinnvoll/flexibel als Zeichenkette zu formatieren.
„All religions are the same: religion is basically guilt, with different holidays.” — Cathy Ladman
Benutzeravatar
__blackjack__
User
Beiträge: 13110
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

@Michaela81: Als ich antwortete war das „Edit“ noch nicht da: Also was zum gezeigten Code:

Der ist fehlerhaft! Die da wird `timef` ausgegeben ohne das überhaupt sicher ist, dass das in dem ganzen ``try``/``except:``-Wust *ohne konkrete Ausnahmen* überhaupt gesetzt wurde. Das kann also zu einem `NameError` führen, im ersten Schleifendurchlauf, oder das der Wert eines vorherigen Schleifendurchlaufs noch einmal ausgegeben wird.

Die nackten ``except:`` ohne konkrete Ausnahem müssen da raus. Dringend.

Bei der Pfadangabe sind \ enthalten aber das ist kein ”rohes” Zeichenkettenliteral. Ist also eher Glück, dass da keine \-Kombination mit spezieller Bedeutung enthalten ist.

In neuem Code würde man `pathlib` statt `os.path` verwenden. Wobei ich mich hier frage ob es gewollt ist, dass der Code einfach nichts macht wenn das keine Datei ist.

Dateien öffnet man wo es möglich ist am besten zusammen mit der ``with``-Anweisung.

Beim öffnen von Textdateien sollte man explizit eine Kodierung angeben.

Es ist hier nicht nötig erst die komplette Datei in den Speicher zu lesen, wenn man dann nur sequenziell über die Zeilen geht.

`k` und `format3` werden definiert, aber nicht verwendet.

Bei den regulären sind wieder \ enthalten und die machen keinen Sinn, weil die absolut keinen Effekt haben, denn die werden für die Erstellung von den Zeichenketteninhalten verwendet und sind *nicht* Bestandteil des Musters für den regulären ausdruck. "\#" ist also das gleiche wie "#" und "\." ist das gleiche wie ".". Der "\#"-Fall würde auch bei einem Muster keinen Sinn machen, denn das #-Zeichen hat in regulären keine besondere Bedeutung. Bei "\." macht es allerdings einen Unterschied ob der \ bestandteil des Musters ist oder nicht, denn der "." hat eine besondere Bedeutung in regulären Ausdrücken.

`re.search()` um einfach nur feste Zeichenkettenteile zu suchen ist Kanonen auf Spatzen. Dafür gibt es den ``in``-Operator, also statt ``if re.search("min", ...):`` einfach ``if "min" in ...:``.

Ich würde da auch eher *einen* regulären Ausdruck schreiben, der alle Fälle abdeckt, und mit benannten Gruppen um die einzelnen Werte heraus zu holen.

Ungetestet:

Code: Alles auswählen

#!/usr/bin/env python3
import re
from pathlib import Path

LENGTH_RE = re.compile(
    r"##  Laenge:(?P<minutes> \d+ min)?(?P<seconds> \d+ s)?"
    r"(?P<milliseconds> \d+ ms)?  ##"
)


def format_duration(milliseconds):
    seconds, milliseconds = divmod(milliseconds, 1000)
    minutes, seconds = divmod(seconds, 60)
    hours, minutes = divmod(minutes, 60)
    return f"{hours:02d}:{minutes:02d}:{seconds:02d}.{milliseconds:04d}"


def main():
    tracks_file_path = Path(R"F:\Python\www.media")
    if tracks_file_path.is_file():
        with tracks_file_path.open(encoding="utf-8") as lines:
            for match in filter(bool, map(LENGTH_RE.search, lines)):
                duration = (
                    int(match["minutes"] or 0) * 60_000
                    + int(match["seconds"] or 0) * 1000
                    + int(match["milliseconds"] or 0)
                )
                print(format_duration(duration))


if __name__ == "__main__":
    main()
„All religions are the same: religion is basically guilt, with different holidays.” — Cathy Ladman
Michaela81
User
Beiträge: 18
Registriert: Samstag 5. September 2020, 18:47

Danke fürs drüber schauen.
Ich muss das erstmal alles überdenken, auf alle Fälle ist deine Antwort lehrreich für mich.
Wobei ein paar Dinge im Code tatsächlich Reste aus diversen Versuchen sind, so eben auch k und format3.
Michaela81
User
Beiträge: 18
Registriert: Samstag 5. September 2020, 18:47

__blackjack__ hat geschrieben: Montag 22. November 2021, 17:36
Die da wird `timef` ausgegeben ohne das überhaupt sicher ist, dass das in dem ganzen ``try``/``except:``-Wust *ohne konkrete Ausnahmen* überhaupt gesetzt wurde.
Die Ausgabe diente mir nur zur Kontrolle ob das gewünschte Ergebniss erzielt wurde, später ist das nicht mehr vorhanden
__blackjack__ hat geschrieben: Montag 22. November 2021, 17:36 Die nackten ``except:`` ohne konkrete Ausnahem müssen da raus. Dringend.
Yep, sehe ich ohne wenn und aber ein
__blackjack__ hat geschrieben: Montag 22. November 2021, 17:36 Bei der Pfadangabe sind \ enthalten aber das ist kein ”rohes” Zeichenkettenliteral. Ist also eher Glück, dass da keine \-Kombination mit spezieller Bedeutung enthalten ist.
Ich versuche meist oft Probleme in Teilstücken zu lösen. Der angegebene Code ist ist so ein Teilstück und diese Pfadangabe ist nur ein Behelfsmittel. Der wirkliche Pfad wird während des Programmablaufs ermittelt.
__blackjack__ hat geschrieben: Montag 22. November 2021, 17:36 In neuem Code würde man `pathlib` statt `os.path` verwenden. Wobei ich mich hier frage ob es gewollt ist, dass der Code einfach nichts macht wenn das keine Datei ist.
Ja, das ist gewollt. Weil im kompletten Programm erst abgeftagt wird ob der zu prüfende Ordner überhaupt Sounddateien enthält und in der Folge Mediainfo aufgerufen wird um die Datei mit den Längenangaben zu erstellen.
__blackjack__ hat geschrieben: Montag 22. November 2021, 17:36 Dateien öffnet man wo es möglich ist am besten zusammen mit der ``with``-Anweisung.
Werde ich beherzigen, aber warum ist das besser?
__blackjack__ hat geschrieben: Montag 22. November 2021, 17:36 Beim öffnen von Textdateien sollte man explizit eine Kodierung angeben.
Sich das anzugewöhnen kann sicher so manche Fehlersuche ersparen. Ich bin grundsätzlich davon ausgegangen das Dwährend des Programablaufs erstellte Dateien die bestehende Codierung benutzen und auch wieder einlesen. In meinem Fall wäre das cp1252..
__blackjack__ hat geschrieben: Montag 22. November 2021, 17:36 Es ist hier nicht nötig erst die komplette Datei in den Speicher zu lesen, wenn man dann nur sequenziell über die Zeilen geht.
Ich bin bei weitem noch nicht gut genug in Python (wie du selber bemerkt hast), um kompliziertere Strukturen zu erzeugen. Vor dem Durchlaufen mit meinem Code wurde diese Datei schonmal durchlaufen um die Dateigrössen zusammen zu rechnen. Es ist mit Sicherheit umständlich, und ich hätte später auch versucht das in einem Durchlauf zu machen. Nichts desto trotz, natürlich hast du auch damit recht
__blackjack__ hat geschrieben: Montag 22. November 2021, 17:36 `k` und `format3` werden definiert, aber nicht verwendet.
Hatte ich schon geschrieben, Überbleibsel vom Testversuchen (die ich hoffentlich noch bemerkt und entfernt hätte)
__blackjack__ hat geschrieben: Montag 22. November 2021, 17:36 Bei den regulären sind wieder \ enthalten und die machen keinen Sinn, weil die absolut keinen Effekt haben, denn die werden für die Erstellung von den Zeichenketteninhalten verwendet und sind *nicht* Bestandteil des Musters für den regulären ausdruck. "\#" ist also das gleiche wie "#" und "\." ist das gleiche wie ".". Der "\#"-Fall würde auch bei einem Muster keinen Sinn machen, denn das #-Zeichen hat in regulären keine besondere Bedeutung. Bei "\." macht es allerdings einen Unterschied ob der \ bestandteil des Musters ist oder nicht, denn der "." hat eine besondere Bedeutung in regulären Ausdrücken.
Beim # war ich mir erstmal nicht sicher ob das nicht eine Sonderbewertung hat, deswegen hatte ich erstmal maskiert, da mir klar war das das zumindest auch nicht schadet. Mit dem re.sub("\..*","",timef), das du vermutlich meinst, sollte bei 00:00:05.330000 ab dem Punkt beliebig viele Zeichen gelöscht werden. Also die Millisekunden. Besser wäre wohl gewesen ab dem Punkt eine beliebig lange Zahlenfolge
__blackjack__ hat geschrieben: Montag 22. November 2021, 17:36 `re.search()` um einfach nur feste Zeichenkettenteile zu suchen ist Kanonen auf Spatzen. Dafür gibt es den ``in``-Operator, also statt ``if re.search("min", ...):`` einfach ``if "min" in ...:``.
Ohje, kannte ich nicht.
__blackjack__ hat geschrieben: Montag 22. November 2021, 17:36 Ich würde da auch eher *einen* regulären Ausdruck schreiben, der alle Fälle abdeckt, und mit benannten Gruppen um die einzelnen Werte heraus zu holen.
Hatte ich versucht, hat erstmal nicht geklappt. Da ich mich zu lange mit Details aufhalten wollte, habe ich das mit zwei Ausdrücken umgangen.

Alles im Allem hast du in allen Punkten Recht. Ich kann dich nur um Nachsicht bitten. Ich kann mir sehr gut Vorstellen wie oft du über stümperhaften Code wie meinen nur noch verzweifelt den Kopf schüttelst. Um so mehr schätze ich die Hilfe die Du und andere hier trotzdem geben. Danke (Auch dafür das du es dir verkniffen hast auf den fehlenden SheBang hinzuweisen)

Noch schnell zu deinem Code, ein erster Durchlauf gibt einen Fehler aus. Schadet aber vielleicht nicht, so muss ich versuchen den besser zu verstehen und den Fehler berichtigen zu können.

Code: Alles auswählen

Traceback (most recent call last):
  File "<pyshell>", line 32, in <module>
  File "<pyshell>", line 26, in main
ValueError: invalid literal for int() with base 10: ' 4 min'
Benutzeravatar
__blackjack__
User
Beiträge: 13110
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

Das die Ausgabe von `timef` nur zur Kontrolle diente, ändert ja nichts am Fehler. Dann wird zur Kontrolle eventuell was falsches ausgegeben. Das ist ja auch nicht hilfreich. Und statt dem `print()` würdest Du dann ja später etwas anderes machen was dann einen potentiell falschen Wert verwendet.

Probleme in Teilstücken zu lösen ist gut. Diese Teillösungen steckt man dann üblicherweise in Funktionen. Damit man sie leicht separat testen und entwickeln kann. Und sich bei den grösseren Teillösungen und der Gesamtlösung auf getestete Funktionen abstützen zu können.

Dass Du schon mal oder noch mal über die Zeilen laufen wolltest, konnte ich an dem gezeigten Code ja nicht sehen. Wir können nur kommentieren was wir sehen/kennen. Ist ja auch nicht so schwer das wieder auf erst komplett einlesen umzuschreiben.

Gut das ich ungetestet drüber geschrieben habe. Jetzt ist es wenigstens soweit getestet, dass es mit den Beispielzeilen funktioniert. Ich hatte die Gruppen nicht auf die eigentliche Zahl beschränkt, sondern nur auf den Teil wo Leerzeichen, Zahl, und Einheit erfasst wird.

Code: Alles auswählen

#!/usr/bin/env python3
import re
from pathlib import Path

LENGTH_RE = re.compile(
    r"##  Laenge:( (?P<minutes>\d+) min)?( (?P<seconds>\d+) s)?"
    r"( (?P<milliseconds>\d+) ms)?  ##"
)


def format_duration(milliseconds):
    seconds, milliseconds = divmod(milliseconds, 1000)
    minutes, seconds = divmod(seconds, 60)
    hours, minutes = divmod(minutes, 60)
    return f"{hours:02d}:{minutes:02d}:{seconds:02d}.{milliseconds:04d}"


def parse_durations(lines):
    return (
        int(match["minutes"] or 0) * 60_000
        + int(match["seconds"] or 0) * 1000
        + int(match["milliseconds"] or 0)
        for match in filter(bool, map(LENGTH_RE.search, lines))
    )


def main():
    tracks_file_path = Path(R"F:\Python\www.media")
    tracks_file_path = Path("test.txt")
    if tracks_file_path.is_file():
        with tracks_file_path.open(encoding="utf-8") as lines:
            for duration in parse_durations(lines):
                print(format_duration(duration))


if __name__ == "__main__":
    main()
Ich würde die Millisekunden ja nicht einfach verwerfen, insbesondere wenn Du die Zeiten aufaddieren willst, denn die können sich ja durchaus zu Sekunden aufaddieren.
„All religions are the same: religion is basically guilt, with different holidays.” — Cathy Ladman
Michaela81
User
Beiträge: 18
Registriert: Samstag 5. September 2020, 18:47

Yep, Danke nochmal.
Das versucht wird Dinge wie "4 min" mal 60_000 zu berechnen habe ich rausbekommen, nicht aber wie man das behebt.
Der neue Code von dir sagt keinen Fehler mehr.
Die Zeile
tracks_file_path = Path("test.txt")
habe ich rausgenommen, jetzt scheint er gut zu klappen

Halt, nein.
Es werden ja wieder nur die einzelnen Tracklängen angezeit. Muss nochmal genauer anschauen
Michaela81
User
Beiträge: 18
Registriert: Samstag 5. September 2020, 18:47

Habe folgenden Abschnitt mal provisorisch abgeändert, die Variable full_time eingeführt, die millisec darin zusammengezählt und das Ergebniss an die Format Funktion zum Rückwandeln geschickt.
Vermutlich nicht die eleganteste Lösung, aber es scheint zu klappen:

Code: Alles auswählen

def main():
    full_time = int()
    tracks_file_path = Path(R"F:\Python\www.media")
    if tracks_file_path.is_file():
        with tracks_file_path.open(encoding="utf-8") as lines:
            for duration in parse_durations(lines):
                full_time = full_time + duration                
                print(format_duration(full_time))
Antworten