MachMichVoll (MP3 zu Stick kopieren bis er voll ist)

  • Ich bin mir jetzt nicht ganz sicher, ob das hier der richtige Platz dafür ist. Falls nicht, bitte verschieben.

    Was macht das Skript?
    Kurze Antwort: Es kopiert so viele Hörspiel-Episoden auf einen USB-Stick bis dieser voll ist. Bei erfolgreichen Kopiervorgang, werden die Daten auf der Festplatte verschoben.

    Lange Antwort:

    Spoiler anzeigen

    Die Geschichte zum Skript ist ganz simpel. Ich liebe Hörspiele. Und weil ich auf Arbeit Zugriff auf ein MP3-fähiges Autoradio habe, nehme ich mir diese mit um sie dort zu hören. Am Anfang hatte ich nur 4 Ordner die ich auf neue Episoden prüfen musste. Mitterweile sind aber über 20 und es macht überhaupt keinen Spaß diese per Hand zu überprüfen.

    Inhalt Liste.txt (Auszug)

    Spoiler anzeigen

    Van Dusen
    Ghostsitter
    John Sinclair
    Midnight Tales
    Faith - The Van Helsing Chronicles

    Als ungelernter Anfänger habe ich dazu noch ein paar Fragen:

    1) Was sagt ein Profi zu meinem Skript? Was würdet ihr anders machen?

    2) Dateien lesen und schreiben kann ich ja problemlos mit z.B. FileReadLine, FileWriteLine, _FileReadToArray und so weiter und so fort. Wozu existiert dann FileOpen bzw. FileClose? Wenn diese beiden Befehle nicht mit drin stehen, funktioniert trotzdem alles so wie es soll.

    3) Beim If-Block If $KopieReturn = 0 Then hab ich in einer früheren Version If @error Then benutzt. Das hat aber nicht funktioniert und ich kapiere das überhaupt nicht. Erst hab ich @error als universales Mittel bei Fehlern gehalten. Also wie ein boolscher Wert (true/false). Aber je mehr ich davon in der Hilfe lese, desto nutzloser wird das Ding. Den Fehler kann ich ja auch mit jeden anderen x-beliebigen Variable auslesen. Was ich dann ja auch mit $KopieReturn gemacht habe. Kann mir jemand das ganz simpel erklären oder ein Beispiel nennen, wo man unbedingt @error braucht?

    Danke!

  • Ich finde es klasse!
    Es ist sowohl übersichtlich - auch ein fremder Betrachter weiß sehr schnell was das Skript wie macht und zum anderen hast du Fehlermanagement implementiert um auf verschiedene ungeplante Dinge zu reagieren.
    Und da das Skript offensichtlich genau das macht, was es soll, ist eigentlich schon 100% auf der Liste abgearbeitet.

    Um deine Übersichtlichkeit noch weiter zu erhöhen hätte ich ein paar Vorschläge.
    Wichtig ist aber, dass diese an der Funktionalität selbst nichts ändern - es geht eher um Code-Ästhetik.

    Aktuell liest du die die Datei mit der Funktion _FileReadToArray() ein.
    Standardmäßig gibt diese die Anzahl der Elemente im ersten Element ($Array[0]) zurück.
    Damit iteriert man über alle Elemente des Arrays mit der Form For $i = 1 To $Array[0] anstatt per For $i = 0 To Ubound($Array) - 1.
    Das kann hübscher sein, hat aber einen Haken: Du nimmst die Schleife um einzeln auf die ganzen Elemente des Arrays zuzugreifen.
    Dazu verwendest du (logischerweise) die Form $HSPSerieTitel[$Titelnummer].
    Es gibt aber noch eine andere Form der For-Schleife - nämlich die For-In-Schleife.
    Diese geht einfach über alle Elemente des Arrays und schmeißt diese in eine benannte Variable.
    Man spart sich damit die Index-Behandlung und das ganze wird deutlich übersichtlicher.
    In deinem Fall hier sollte aus meiner Sicht diese Variante die schönere sein (Beispiel folgt unten), denn anstatt immer in der Schleife zu schreiben $HSPSerieTitel[$Titelnummer], reicht dann ein $AktuellerTitel und $HSPSerieTitel kann ganz verschwinden.

    Dazu muss man aber die Anzahl aus dem ersten Element bekommen, sonst behandelt die For-In-Schleife diese ebenfalls als normales Element.
    Bei _FileReadToArray() kann man dazu das Flag $FRTA_NOCOUNT setzen oder man nimmt gleich das Pendant FileReadToArray (ohne Unterstrich).

    Bei _FileListToArray() bekommt man die Anzahl hingegen leider nicht so einfach weg, so dass du das mit deiner For-To-Schleife schon auf dem besten Weg bist (ich persönlich verwende stattdessen aus dem Grund direkt FileFindFirstFile/FileFindNextFile - aber das muss hier nicht sein).

    Nun zu deiner Frage mit FileOpen/FileClose.
    Man kann eine Datei "öffnen", dann wird ein sogenannter FilePointer auf die Datei (genauer auf eine bestimmte Stelle in der Datei) erstellt und gehalten.
    Das muss im Grunde immer gemacht werden wenn mit einer Datei gearbeitet wird.
    Manuell macht das in AutoIt aber eigentlich nur Sinn wenn du mehrere Operationen auf eine Datei ausführen möchtest (z.B. mehrere FileRead oder FileReadLine oder FileWrite usw.).
    Die meisten Funktionen in AutoIt machen nämlich selbst das FileOpen/FileClose wenn man nur den Dateinamen angibt.
    So auch _FileReadToArray(). Das heißt du brauchst hier gar nicht FileOpen/FileClose, da sich _FileListToArray schon darum kümmert.
    Und in deinem Fall war das leider auch sowieso wirkungslos, da man den Rückgabewert von FileOpen (das sogenannte FileHandle) dann in den File-Funktionen weiterverwendet anstatt dem Dateinamen.

    Das heißt konkret den Anfang deiner Region 2 könnte man folgendermaßen umschreiben:

    AutoIt
    #Region 02 - HSP
    
    ;große Schleife Serientitel aus Datei
    For $AktuellerTitel In FileReadToArray($ListeTXT)
    
    	;Ordner einer Serie erfassen
    	$ArrayEpisodenOrdner = _FileListToArray($HoerspielRoot & $AktuellerTitel, "*", $FLTA_FOLDERS)

    Aus meiner Sicht übersichtlicher, aber am Ende immer noch eine Geschmacksfrage.
    Der Effekt bleibt dennoch der gleiche - das Skript macht weiter was es soll.

    3) Beim If-Block If $KopieReturn = 0 Then hab ich in einer früheren Version If @error Then benutzt. Das hat aber nicht funktioniert und ich kapiere das überhaupt nicht.

    Das ist ganz von der jeweiligen Funktion abhängig. Eine Funktion hat 3 Ausgabemöglichkeiten (von ByRef-Parametern mal abgesehen): Den Return-Wert, den @error-Wert und den @extended-Wert.
    Ob und wie diese genutzt werden ist Sache der Funktion. Bei Funktionen die keinen wirklichen Rückgabewert benötigen (wie eben DirCopy) wird dort gerne ein Erfolgswert zurückgegeben.
    In dem Fall braucht man dann also kein @error mehr, da das schon durch den Return-Wert abgedeckt wird.
    Du musst also für jede Funktion schauen wie ihre Ausgaben funktionieren - daran kommst du nicht vorbei.
    Und ein Beispiel wo man unbedingt @error braucht ist im Grunde jede Funktion wo der Rückgabewert schon belegt ist. Z.B. eben _FileListToArray. Dort muss die Fehlerinformation auf anderem Wege erfolgen, da der Nutzer als Rückgabe ja ein Array erwartet und keinen Erfolgswert.

  • Vielen Dank für Ihre Einschätzung.

    Und Sie treffen bei mir völlig ins Schwarze: Ich habe tatsächlich Probleme mit der Übersichtlichkeit. Gerade bei Schleifen. Viel zu oft erwische ich mich selbst dabei, daß ich z.B. immer wieder dieselben Variablen benutze und irgendwann selbst nicht mehr weiß was da gerade vor sich geht. Da hilft mir diese For-In-Schleife wirklich weiter!

    Auch vielen Dank für die Erklärungen für FileOpen/Close und @error. Obwohl ich mir Letzteres in einer ruhigen Minute nochmal zu Gemüte führen muss. Da habe ich wieder viel zu kurz gedacht.

  • Als Ergänzung:
    @error (und auch @extended) sind nicht nur dafür da, um festzustellen ob ein Fehler aufgetreten, sondern bei vielen Funktionen auch, welcher Fehler.
    Das hilft dann beim programmieren (z.B. bei der Fehlersuche) und kann ggf. auch genutzt werden, um im Code zu entscheiden, wie es bei einem Fehler weitergehen soll.

    Ein Beispiel wäre die StringRegExp -Funktion. Dort steht z.B. in der Hilfe:

    Code
    @error: Meaning 
    0: Array is valid. Check @extended for next offset 
    1: Array is invalid. No matches. 
    2: Bad pattern, array is invalid. @extended = offset of error in pattern. 

    Man kann also aus dem @error und @extended ableiten, was schief gegangen ist und wie man es beheben kann.