Auslesen bestimmter Wörter aus einer html-ähnlichen Datei

Stelle Fragen zur Programmierung mit Autohotkey

Moderator: jNizM

just me
Posts: 9424
Joined: 02 Oct 2013, 08:51
Location: Germany

Re: Auslesen bestimmter Wörter aus einer html-ähnlichen Datei

25 Apr 2018, 11:21

Moin,

wenn das jetzt so läuft wie es soll, solltest Du Dein Skript so lassen.

Ich habe immer noch Probleme mit der Logik des Ganzen.
Eine Veranstaltung mit Sprache wird gefilmt. Gleichzeitig wird im Saal der Text eingeblendet, indem jemand den Text mitliest und bei der nächsten Zeile auf "Weiter" drückt. Dies sind die Übertitel der lvt-Datei. Des Weiteren befinden sich dann die Sprache1 und Sprache2 in der lvt-Datei, in der Beispieldatei deutsch und englisch.
Was soll mir das sagen? Wird die lvt-Datei während der Veranstaltung erstellt oder ist sie die Basis der 'Lesung'?
Ich lese nun die lvt-Datei aus und logge mit, wann derjenige, der die Titel im Saal wechselt, dies tut.
Ich sehe in Deinem Beispiel einer lvt-Datei keinen Hinweis darauf, wann der Text wechselt.
Im Prinzipt ist Ragnars Skript funktionstüchtig. Es könnte unter Umständen geschickt sein, einen einzelnen Akt auszulesen, aber ich kann auch einen langen Titel haben und zerschneide ihn dann in die einzelnen Akte.
Ich habe schon versucht zu erklären, dass Ragnars Skript immer nur die erste Szene des ersten Aktes ausliest. Du brauchst deshalb nie etwas zu 'zerschneiden'. Dir mögen aber Szenen oder ganze Akte fehlen.

Ich kann mir folgendes Szenario vorstellen:

Du hast eine irgendwann entstandene Log-Datei mit Zeitstempeln und eine irgendwann entstandene lvt-Datei. Du versuchst, beide Dateien zu 'synchronisieren' und für jede vorhandene Sprache eine eigene Datei für die Untertitelsteuerung auszugeben. Diese Dateien enthalten je Track eine laufende Nummer, eine Zeitangabe Von --> Bis und die tatsächlich gefüllten <text> Tags der jeweiligen Sprache als einzelne Zeilen. Ist das so richtig?
(Ein paar weitere Kleinigkeiten wären in diesem Fall noch zu diskutieren.)

Ich habe außerdem ein Verständnisproblem mit folgendem Code:

Code: Select all

Var := A_MSec
SetFormat, float, 03.0
Var := Var / 20
Var := Var * 20
Var := Format("{:03}",Var)
if Var contains 1000 
{
	Var :=000
}
Wozu ist das gut? Warum reicht nicht einfach

Code: Select all

Var := Format("{:03}", A_MSec)
? Bei A_MSec >= 990 verlierst Du so fast eine Sekunde.
festigrat
Posts: 54
Joined: 02 Mar 2017, 06:58

Re: Auslesen bestimmter Wörter aus einer html-ähnlichen Datei

25 Apr 2018, 12:59

Hi just me,
danke für Deine Anmerkungen. Ich versuche es nochmal etwas besser zu erklären. Die LVT-Datei existiert als allererstes. Der Text wird dort in beiden Sprachen eigegeben, da er bekannt ist. Während der Veranstaltung wird der Text (maximal 3 Zeilen) zur richtigen Zeit eingeblendet (nur der Übertitel), damit das Publikum mitlesen kann. Wenn nun eine Videoaufnahme von der Veranstaltung gemacht wird und dort der Text beider Sprachen eingeblendet werden soll. muss dieser ja eingegeben werden und der richtige Zeitpunkt bestimmt werden. Dies geschieht ja eigentlich schon während der Veranstaltung. Deswegen war meine Idee, die {Down}und die{Up}-Tasten mitzuloggen, die einen Titel im Saal weiterschalten und auch wieder zurück, falls man sich vertippt hat. Anschließend wähle ich den ersten relevanten Titel als 1. Titel aus und die Uhrzeit des ersten relevanten Tastendruck setze ich auf 00:00:00,000. Dadurch kann ich mir automatisch eine Untertitel-Datei basteln, die ich mit meinem Videoprogramm einlesen kann.

Die Anmerkung, dass nur der erste Akt ausgelesen wird, habe ich erst jetzt richtig verstanden. Hier könnte es tatsächlich zu einem Problem kommen. In meiner Beispiel-LVT-Datei ist der gesamte Text von drei Akten in einen einzigen Akt gepackt worden. Wahrscheinlich wird dies auch in Zukunft so gehandhabt, es könnte dennoch passieren, dass es mehrere Akte gibt. In Ragnars Skript taucht das Wort Akt nicht auf, wo würde man denn den Akt auswählen?

Deine zweite Anmerkung zu den Millisekunden kann ich gut nachvollziehen und hier ist das Skript wirklich nicht gut. Aber ich habe es nicht sauber hinbekommen. Eigentlich will ich etwas sehr einfaches, nämlich dass die Taste {Down} und{Up} geloggt wird, und zwar im Zahlenformat HH:MM:SS,FFF
FFF sind Millisekunden in Schritten von jeweils 20 ms.

Durch folgenden Code bekomme ich die gewünschten Schritte hin:

Code: Select all

Var := A_MSec
SetFormat, float, 03.0
Var := Var / 20
Var := Var * 20
Irgendwann war dann durch Zufall die Zahl 1000 hinter dem Komma, obwohl ich Format("{:03}" eingegeben habe. Deshalb dürften doch nur 3 Stellen da sein, oder? Gebe ich jedoch folgenden Code ein, wird die 1000 nicht zu 000 gemacht. Warum bloß?

Code: Select all

Var := 1000
SetFormat, float, 03.0
Var := Var / 20
Var := Var * 20
Var := Format("{:03}",Var)
MsgBox, %Var%
Damit das Zahlenformat nicht falsch ist, habe ich dann 1000 zu 000 gemacht, da ich dachte, dass 1000 MSec ja einer Sekunde entspricht und diese ist ja schon bei A_Min mitgezählt worden. Dein Einwand stimmt natürlich und von 990 bis 999 ms wäre das wirklich fast eine Sekunde zu wenig und das wäre sehr doof!

Ich rechne ja nur die Msec, den Rest nehme ich mir ja durch A_Hour, A_Min und A_Sec:

Code: Select all

Var1 =,
FileAppend, %A_Hour%:%A_Min%:%A_Sec%%Var1%%Var%;1`n, C:\Autohotkey\Output\%A_YYYY%%A_MM%%A_DD%_UT-Log.csv
Das müsste ich komplett ändern und von 990 bis 999 ms hinter dem Komma 000 anzeigen lassen und eine Sekunde addieren. Aber diese eine Sekunde kann sich ja auch auf die Minuten und Stunden auswirken...
Das ganze ist ja so ähnlich, wie das Zeitskript, dass Du mir geschickt hast, aber ich verstehe es leider nicht ganz. Hast Du hier bitte nochmal einen Tip? Und ist jetzt der ganze Sinn meines Skriptes klar geworden?
festigrat
Posts: 54
Joined: 02 Mar 2017, 06:58

Re: Auslesen bestimmter Wörter aus einer html-ähnlichen Datei

26 Apr 2018, 02:01

Guten Morgen,
ich habe nun endlich den Befehl FormatTime verstanden und damit konnte ich das Problem mit den Millisekunden lösen. Vielleicht geht es auch eleganter, aber ich froh, dass ich es hinbekommen habe:

Code: Select all

Var := A_MSec
FormatTime, Output4, A_Now, HH:mm:ss,%Var% ;Akutelle Zeit zum checken
if Var contains 1000 ; Abfrage, ob es genau 1000 Millisekunden sind
{
	FormatTime, Output1, A_Now, HH:mm:ss,000
	;FileAppend, %Output1%;1`n, C:\Autohotkey\Output\%A_YYYY%%A_MM%%A_DD%_UT-Log.csv
	MsgBox, Fall1
}
else
{
	SetFormat, float, 03.0  ;Umrechnung in Schritten von jeweils 20 ms
	Var := Var / 20
	Var := Var * 20
	Var := Format("{:03}",Var)
	if Var contains 1000 ; Wenn 1000 ms sind, eine Sekunde addieren
	{
		Output2 = %A_Now%
		Output2 +=1, Seconds

		FormatTime, Output2, %Output2%, HH:mm:ss,000
		;FileAppend, %Output2%;1`n, C:\Autohotkey\Output\%A_YYYY%%A_MM%%A_DD%_UT-Log.csv
		MsgBox, Fall2
	}
	else ; trifft bei Werten von 000 bis 989 ms ein
	{
		FormatTime, Output3, A_Now, HH:mm:ss,%Var%
		;FileAppend, %Output3%;1`n, C:\Autohotkey\Output\%A_YYYY%%A_MM%%A_DD%_UT-Log.csv
		MsgBox, Fall3
	}
}


MsgBox, %Output1%%Output2%%Output3% Now: %Output4% ;MsgBox zum Funktionstest
Liebe Grüße!
just me
Posts: 9424
Joined: 02 Oct 2013, 08:51
Location: Germany

Re: Auslesen bestimmter Wörter aus einer html-ähnlichen Datei

26 Apr 2018, 03:48

Moin,

erst einmal zu dem Problem mit den Millisekunden. Ich glaube, Du machst Dir das zu kompliziert.

A_MSec kann nur Werte von 0 bis 999 enthalten, weil der Wert 1000 bereits in der nächsten Sekunde liegen würde. Das ist genau so wie bei der Variablen A_Sec, die nur Werte von 0 - 59 enthalten kann.

Ich gehe davon aus, dass Du das machst, weil das zu steuernde Programm nur Millisekundenwerte in zwanziger Schritten akzeptiert.

Wenn Du damit leben kannst, dass der Wert abgerundet wird, kannst Du das in einer Anweisung erledigen:

Code: Select all

MSec := A_MSec // 20 * 20 ; // = Division mit ganzahligem Ergebnis
Dabei ist ein 'Überlauf' auf 1000 nicht möglich.

Wenn Du lieber runden willst, geht das zunächst einmal auch mit einer Anweisung:

Code: Select all

MSec := (A_MSec + 10) // 20 * 20 ; + 10 zum Runden
Hier kann als Ergebnis aber auch 1000 entstehen, das nachbehandelt werden muss indem man die Millisekunden auf Null setzt und eine Sekunde auf die Zeit addiert. Das geht sehr einfach, wenn man die aktuelle Zeit A_Now bereits gespeicher hat:

Code: Select all

Now := A_Now
MSec := (A_MSec + 10) // 20 * 20
If (MSec > 980)
{
   MSec := 0
   Now += 1, S
}
Jetzt musst Du die Variable MSec nur noch mit führenden Nullen auffüllen. Das erledigt die Funktion Format(). Die Formatierungs "{:03}" bedeutet allerdings: "Fülle das Ergebnis mit Nullen auf, wenn es weniger als 3 Stellen hat". Wenn Du einen Wert mit 4 oder mehr Stellen übergibst, wird der unverändert ausgegeben.

Code: Select all

MSec := Format("{:03}", MSec)
Schließlich kannst Du Deinen kompletten Zeitstempel erstellen.

Code: Select all

FormatTime, OutPut, %Now%, HH:mm:ss,%MSec%
Der komplette Code sähe dann je nach Variante so aus:

Code: Select all

; Abrunden
Now := A_Now
MSec := A_MSec // 20 * 20 ; // = Division mit ganzzahligem Ergebnis
MSec := Format("{:03}", MSec)
FormatTime, Zeitstempel, %Now%, HH:mm:ss,%MSec%
MsgBox, %Zeitstempel%

; Runden
Now := A_Now
MSec := (A_MSec + 10) // 20 * 20  ; + 10 zum Runden
If (MSec > 980)
{
   MSec := 0
   Now += 1, S
}
MSec := Format("{:03}", MSec)
FormatTime, Zeitstempel, %Now%, HH:mm:ss,%MSec%
MsgBox, %Zeitstempel%
Noch ein abschließender Hinweis:
Wenn man sich Zeiten mit einer Genauigkeit im Millisekundenbereich aus verschiedenen Zeitvariablen zusammenbastelt, sollte man deren aktuelle Werte möglichst 'gleichzeitig' zwischenspeichern. Sonst erhöht sich das Risiko, dass die zuletzt gespeicherten Werte nicht mehr zu den vorher gespeicherten passen, wenn zwischendurch die Sekunde überläuft. Man kann man solche 'Fehler' aber nicht völlig ausschließen. Als Alternative bietet sich der Aufruf einer Systemfunktion an, die alle benötigten Werte mit nur einem Zugriff über die Systemzeit holt. Wenn Du daran Interessiert bist, kann ich Dir sowas geben.
just me
Posts: 9424
Joined: 02 Oct 2013, 08:51
Location: Germany

Re: Auslesen bestimmter Wörter aus einer html-ähnlichen Datei

26 Apr 2018, 04:27

Nun zum eigentlichen Thema.

Ich glaube, ich habe es jetzt verstanden. Bleiben nur noch zwei 'technische Klarstellungen':
  1. Das aufgenommene Video wird so gestutzt, dass es mit der Einblendung der ersten 'echten' Textzeilen beginnt?
  2. Wie entsteht die zeitliche Lücke in Deinem Beispiel?
    1
    00:00:00,000 --> 00:00:03,960
    Zeile1 deutsch
    (fasls vorhanden) Zeile2 deutsch
    (falls vorhanden) Zeile3 deutsch

    2
    00:00:04,120 --> 00:00:08,160
    Zeile4
    (falls vorhanden) Zeile 5
festigrat
Posts: 54
Joined: 02 Mar 2017, 06:58

Re: Auslesen bestimmter Wörter aus einer html-ähnlichen Datei

27 Apr 2018, 01:19

Hi,
vielen Dank für Deine Anmerkungen zur Zeit. Ich habe das Skript jetzt etwas geändert, so dass ich den ersten Teil weglasse und A_MSec und A_Now direkt hintereinander einlese. Aber Du kennst noch eine Möglichkeit dies mit einer Abfrage geschehen zu lassen? Ist dies kompliziert?

Code: Select all

~Down::

Var := A_MSec
Zeit := A_Now
;FormatTime, Output4, A_Now, HH:mm:ss,%Var% ;Akutelle Zeit zum checken

	SetFormat, float, 03.0  ;Umrechnung in Schritten von jeweils 20 ms
	Var := Var / 20
	Var := Var * 20
	Var := Format("{:03}",Var)
	if Var contains 1000 ; Wenn 1000 ms sind, eine Sekunde addieren
	{
		Output1 = %Zeit%
		Output1 +=1, Seconds

		FormatTime, Output1, %Output1%, HH:mm:ss,000
		FileAppend, %Output1%;1`n, C:\Autohotkey\Output\%A_YYYY%%A_MM%%A_DD%_UT-Log.csv
	}
	else ; trifft bei Werten von 000 bis 989 ms ein
	{
		FormatTime, Output2, %Zeit%, HH:mm:ss,%Var%
		FileAppend, %Output2%;1`n, C:\Autohotkey\Output\%A_YYYY%%A_MM%%A_DD%_UT-Log.csv
	}
Zu Deinen Fragen:
1. Das Video wird nicht gestutzt, sondern der fertige Untertitel wird an die richtige Stelle, nämlich den Einsatz der Sprache, geschoben. Das ist ganz einfach.
2. In diesem Fall wird zwischen 00:00:03,960 und 00:00:04,120 kein Untertitel angezeigt. In meinem Skript würde dies jedoch anders aussehen, nämlich folgendermaßen:

Code: Select all

1
00:00:00,000 --> 00:00:03,960
Zeile1 deutsch
(fasls vorhanden) Zeile2 deutsch
(falls vorhanden) Zeile3 deutsch

2
00:00:03,960 --> 00:00:04,120

3
00:00:04,120 --> 00:00:08,160
Zeile4
(falls vorhanden) Zeile 5
Vom Ergebnis her ist es identisch.
just me
Posts: 9424
Joined: 02 Oct 2013, 08:51
Location: Germany

Re: Auslesen bestimmter Wörter aus einer html-ähnlichen Datei

27 Apr 2018, 09:55

Moin,
Aber Du kennst noch eine Möglichkeit dies mit einer Abfrage geschehen zu lassen? Ist dies kompliziert?
Was ich mir dachte, ist an sich nicht kompliziert. Inzwischen ist mir aber eine Alternative eingefallen.

In AHK gibt es die interne Variable A_TickCount. Sie enthält die Zeit in Millisekunden, die seit dem Start des Systems verstrichen ist. Es sollte deshalb reichen, in der Log-Datei den aktuellen Wert von A_TickCount zu speichern.

Weil Du die Dauer der Einblendung ohnehin aus den Differenzen der Einträge des aktuellen und des folgenden Log-Satzes bildest, könntest Du dann die Millisekunden direkt aus den Werten der Log-Sätze berechnen, d.h. auf die Funktion ConvertToMilliseconds() vollständig verzichten.

Wenn Du das änderst, wäre es gut, wenn bereits die Anzeige der ersten Einblendung protokolliert wird, im Beispiel "Bitte schalten Sie Ihr Mobiltelefon ab. Vielen Dank!" (falls Du das nicht sowieso machst). Außerdem ist es besser, wenn auf dem Rechner die Windows "Schnellstartoption" nicht aktiv ist (wird unter Win 10 standardmäßig aktiviert und (wurde zumindest) bei den großen Updates ungefragt reaktiviert). Ansonsten kann A_TickCount alle 49,7 Tage überlaufen und zurück auf Null gehen. Wenn Du Pech hast, geschieht das genau während einer Protokollierung. Wenn die Schnellstartoption aktiv bleiben soll, sollte auf dem Rechner vor dem Start der Protokollierung ein echter Neustart ausgeführt werden.

Außerdem habe ich mir noch einmal alle Deine Informationen durchgelesen und mir auch die letzte eingestellte Fassung Deines Skript genauer angeschaut. Es mag besser sein, wenn Dein Skript so prorammiert ist, dass Du es nachvollziehen kannst. Dennoch habe ich erst einmal zwei Anmerkungen:
  • Wenn Du Funktionen mit identischem Code mehrfach aufrufen willst, musst Du die nicht mehrfach mit unterschiedlichen Namen im Code ablegen. Du kannst deshalb ConvertToMilliseconds2() etc. wieder entfernen und die Aufrufe durch ConvertToMilliseconds() ersetzen. Außerdem ist es nicht üblich, die Funktionsdefinitionen mitten im aufrufenden Code unterzubringen.
  • Die Berechnung der Start- und Endezeiten für die Untertitel-Dateien sieht z.Zt. so aus:
    • Ersten Ausgabesatz ermitteln und Startzeit als "Versatz" speichern.
    • Schleife
      • Satz aus Log-Datei lesen und Start berechnen
        Beim ersten Mal ist das der Satz, der schon für die Bestimmung des Starts der Ausgabe gelesen wurde.
        Danach ist es der Satz, der schon für die Bestimmung des Endes der Anzeige gelesen wurde.
      • Folgesatz aus Log-Datei lesen und Ende berechnen.
Die Sätze der Log-Datei werden dabei zweimal gelesen. Dabei wird aus der Zeitangabe des Log-Satzes zuerst das Ende des aktuellen Satzes und danach der Start des folgenden Satzes berechnet. Ende und Start sind also identisch und die Rechnerei für Start ist unnötig. Start kann einfach mit dem errechneten Ende des Vorsatzes gefüllt werden. Für den ersten Ausgabesatz, dessen Start ja immert 00:00:00,000 ist, kann der Endwert vor Beginn der Schleife mit diesem Wert vorbelegt werden.

Um zu zeigen, was ich meine, habe ich diesen Teil Deines Skripts überarbeitet. Ich hoffe, dass ich keine Fehler eingebaut habe, denn ich konnte es nicht testen:

Code: Select all

break_outer4:

Versatz := word_a1

; Eingefügt, das muss nur einmal berechnet werden
Versatz := ConvertToMilliseconds(Versatz)

ZeileLVT := 1

Nummer := 1

; Eingefügt, Grundstellung für Ende, wird als Startwert für die erste Zeile der Ausgabe genutzt
Ende := "00:00:00,000"

Loop
{

   ; Lvt-Satz lesen
   FileReadLine, GanzeZeileLVT, UT-LVT.txt, %ZeileLVT%
   StringSplit, word_arrayLVT, GanzeZeileLVT, `|, .
   word_arrayLVT2 := SubStr(word_arrayLVT2, 1 , -1)
   word_arrayLVT3 := SubStr(word_arrayLVT3, 1 , -1)
   word_arrayLVT4 := SubStr(word_arrayLVT4, 1 , -1)
   word_arrayLVT5 := SubStr(word_arrayLVT5, 1 , -1)
   word_arrayLVT6 := SubStr(word_arrayLVT6, 1 , -1)
   word_arrayLVT7 := SubStr(word_arrayLVT7, 1 , -1)

   ; Zeilennumer des nächsten Satzes der Lvt-Datei bestimmen
   ZeileLVT += word_array2

   ; Start mit dem Ende des vorherigen Satzes versorgen
   Start := Ende

   ; Zeilennummer der Log-Datei auf den nächsten Satz setzen
   Zeile += 1

   ; Log-Satz lesen
   FileReadLine, GanzeZeile, %SelectedFile%, %Zeile%
   StringSplit, word_array, GanzeZeile, `;, .

   ; Fertig?
   If word_array1 contains asdfjklö
   {
      Goto break_outer5
   }

   ; Ende berechnen
   Millisekunden := ConvertToMilliseconds(word_array1)
   Differenz := Millisekunden - Versatz
   Ende := ConvertToHHMMSSMS(Differenz)

   ; Fehlerprüfung übernommen, kann das wirklich sein???
   If (Ende = "00:00:00,000")
   {
      Ende := Start
   }

   ; Satz für erste Sprache ausgeben
   FileAppend %Nummer%`n, Sprache1.srt
   FileAppend, %Start% --> %Ende%, Sprache1.srt
   FileAppend, `n%word_arrayLVT2%`n, Sprache1.srt
   FileAppend, %word_arrayLVT3%`n, Sprache1.srt
   FileAppend, %word_arrayLVT4%`n, Sprache1.srt

   ; Satz für zweite Sprache ausgeben
   FileAppend %Nummer%`n, Sprache2.srt
   FileAppend, %Start% --> %Ende%, Sprache2.srt
   FileAppend, `n%word_arrayLVT5%`n, Sprache2.srt
   FileAppend, %word_arrayLVT6%`n, Sprache2.srt
   FileAppend, %word_arrayLVT7%`n, Sprache2.srt

   ; Nummer für Ausgabedateien erhöhen
   Nummer += 1

}

break_outer5:
MsgBox, Fertig!
ExitApp

; Funktionen ans Ende geschoben
ConvertToMilliseconds(HHMMSSMS)
{
    HH := SubStr(HHMMSSMS, 1, 2) * 1000 * 3600
    MM := SubStr(HHMMSSMS, 4, 2) * 1000 * 60
    SS := SubStr(HHMMSSMS, 7, 2) * 1000
    MS := SubStr(HHMMSSMS, 10)
    return HH + MM + SS + MS
}

ConvertToHHMMSSMS(Milliseconds)
{
    HH := Milliseconds // 3600000
    remain := Mod(Milliseconds, 3600000)
    MM := remain // 60000
    remain := Mod(remain, 60000)
    SS := remain // 1000
    remain := Mod(remain, 1000)
    MS := Mod(remain, 1000)
    return Format("{:02}:{:02}:{:02},{:03}", HH, MM, SS, MS)
}
Du kannst Dir das ja mal anschauen. Ich habe ein paar Kommentare eingefügt, um es verständlicher zu machen. Wenn Du es willst, können wie auch noch weitermachen. Wenn Du aber lieber bei Deinem Code bleiben willst, ist das natürlich auch in Ordnung.

Return to “Ich brauche Hilfe”

Who is online

Users browsing this forum: No registered users and 20 guests