Beiträge von AspirinJunkie

    mko

    Jo danke - ich werde mich wohl zwingen MustDeclareVars mehr zu verwenden.


    Zur Diskussion ob die Deklaration innerhalb oder außerhalb der Schleife besser aufgehoben wäre würde ich gern etwas ausholen, da ein "ist schlechter Stil" in der Regel als Begründung unbefriedigend ist:
    Local/Global/Dim vor dem Variablennamen bedeuten immer eine [Neu]Deklaration einer Variable.
    Das heißt im Speicher wird Platz für eine neue Variable reserviert und dieser Speicher entsprechend vorbereitet.
    Da AutoIt-Variablen intern etwas aufwendiger aufgebaut sind, ist diese Deklaration also in AutoIt besonders aufwendig.

    Das reine Zuweisen eines Wertes ("Definition") hingegen ist simpler, da einfach nur der Wert an die bereits vorhandene Speicherstelle geschrieben wird.


    Sprich: Es ist effizienter eine Variable nur einmal zu deklarieren und danach nur noch ihren Wert entsprechend zu ändern.
    Und um es ganz anschaulich zu machen hier ein kurzes Beispiel um den Effekt zu sehen:


    Zu der Dim/Global/Local-Diskussion:
    Stimmt schon alles.
    Das Hauptargument gegen Dim ist eben, dass man beim Codeteil nicht weiß ob es nun eine globale oder eine lokale Variable werden soll.
    Es kommt schlicht darauf an in welchem Kontext die Funktion ausgeführt wird.
    Das führt natürlich dazu, dass eine Funktion 100x funktioniert weil die Variable local wird und beim 101. Skript geht auf einmal alles schief weil in dem Skript auf einmal jemand eine globale Variable mit selben Namen definiert hat.
    Dim ist daher tatsächlich zu vermeiden wenn man nicht einen ganz bewussten Anwendungsfall hierfür hat.

    Genau so ein arg konstruiertes Beispiel wo der Einsatz von Dim Sinn macht hätte ich sogar zufällig:
    Wenn man eine ByRef-Variable in seiner Funktion zur Rückgabe von Werten als Array verwenden möchte.

    Hier kann es jedoch sein, dass der User entweder ein Array oder eben doch einen Skalar übergibt.

    Bei einem Skalar kann man daraus jedoch kein Array mehr basteln, deswegen muss der User eine Array-Variable übergeben.
    Mit Dim hingegen kann man dies umgehen:

    Code
    #include <Array.au3>
    
    Global $x
    Test($x)
    
    _ArrayDisplay($x)
    
    Func Test(ByRef $aArray)
        Dim $aArray[2] = ["Ein", "Test"]
    EndFunc

    Die -1 ist im Grunde eigentlich gar nicht wirklich falsch.
    Bei Integerdatentypen wird oftmals das erste (bzw. letzte - je nach Sicht) Bit dafür genommen um das Vorzeichen festzulegen.

    Ein Int32, welches die Bitfolge 0xFFFFFFFF interpretiert ist also tatsächlich ganz korrekt -1

    Die höchste darstellbare Int32-Zahl wäre ergo hingen 0x7FFFFFFF.

    Eine Int64-Zahl hingegen hat ein paar Stellen mehr. Dort ist die höchste darstellbare Zahl die 0x7FFFFFFFFFFFFFFF

    Und hier sieht man auch, dass der Bereich für 0xFFFFFFFF noch im positiven Bereich liegt.
    Daher entspricht die Zahl als Int64 kodiert der 4294967295 und als Int32 der -1.


    Wie macht man nun AutoIt klar, ob man es nun mit einer Int32 oder einer Int64 zu tun haben will?
    Entweder über die expliziten Parameter von z.B. Dec() oder Number() oder man gibt einfach mehr Stellen an, als in eine Int32 passen - nämlich so:

    AutoIt
    Local $iVal = 0x0FFFFFFFF
    ConsoleWrite('Dec("0FFFFFFFF", 0) ............ = ' & Dec("0FFFFFFFF", 0) & @CRLF)
    ConsoleWrite('Dec("0FFFFFFFF") ............... = ' & Dec("0FFFFFFFF") & @CRLF)
    ConsoleWrite("ConsoleWrite $iVal ............. = " & $iVal & @CRLF)

    Das scheint eine Art Bug zu sein.
    Dec kennt noch einen flag-Parameter welcher den Datentyp angibt.
    Wenn man ihn weglässt sollte dieser automatisch auf 0 stehen ($Number_Auto) - und dort würde er schauen wieviele Stellen die Hex-Zahl hat und auf $Number_64BIT ausweichen.

    So steht es zumindest in der Hilfe.


    Stattdessen scheint er aber per Default auf $NUMBER_32BIT zu gehen und dort kommt zurecht der Fehler, da es außerhalb des Wertebereiches eines 32 Bit-Integers liegt.

    Abhilfe sollte also schaffen, explizit das flag auf Auto zu setzen:

    AutoIt
    Dec("FFFFFFFF",0)

    Das Problem hat eigentlich nichts mit AutoIt's Umwandlung in Int64 (oder auch Int32) zu tun, sondern in der merkartigen Behandlung von Datentypen in der Windows-API hier im speziellen Fall.

    Was meine ich damit?

    Die _WinAPI_GetDriveNumber() ruft die Windows-API-Funktion DeviceIoControl auf. Was diese zurückgibt wird primär über den Parameter dwIoControlCode gesteuert.
    Die AutoIT-UDF trägt hier hart die 0x002D1080 ein. Dieser Wert entspricht (etwas schwierig nachzuvollziehen) der API-Konstante IOCTL_STORAGE_GET_DEVICE_NUMBER.

    Damit ergibt sich, dass der Parameter lpOutBuffer vom Typ STORAGE_DEVICE_NUMBER sein muss.

    Diese ist in der WinIoCtl.h folgendermaßen definiert:

    Code
    typedef struct _STORAGE_DEVICE_NUMBER {
        DEVICE_TYPE DeviceType;
        DWORD       DeviceNumber;
        DWORD       PartitionNumber;
    } STORAGE_DEVICE_NUMBER, *PSTORAGE_DEVICE_NUMBER;

    Die PartitionNumber - also dein fraglicher Parameter in deiner Struktur ist dabei als DWORD-Typ definiert.

    Was ist ein DWORD? - in der WinDef.h wird dword folgendermaßen definiert: typedef unsigned long DWORD;

    Bis hierhin stimmt die UDF auch (bis auf den kleinen Aspekt, dass sie ulong statt dword genommen haben - was aber äquivalent ist).


    Die wirklich interessante Frage ist jedoch: Wie kommt Microsoft darauf einem unsigned integer einen Wert von -1 zuzuweisen?
    Dieser Wert liegt außerhalb vom Wertebereich von dword. Ist also defacto falsch.
    Im Wertebereich von 0 bis 2.147.483.647 tut dies im Grunde auch gar nicht weh, da diese Zahlen auf binärer Ebene als dword und int völlig gleich sind (nämlich in beiden Fällen 00000000 - 7FFFFFFF).
    Die Weiterbehandlung ist daher nicht wirklich problematisch.
    Ekelhaft wird es erst wenn man Zahlen außerhalb dieses Bereiches nimmt (also bei denen das 1. Bit gesetzt wird) wie eben die -1.

    Das ist (meiner Meinung nach) für dword schlicht falsch - es tut in C aber schlicht nicht wirklich dolle weh, da folgendes weiterhin funktioniert:

    Code
    (DWORD)4294967295 == -1   // ergibt true

    Man kann also munter weiter gegen -1 testen obwohl der Wertebereich von dword dafür gar nicht ausgelegt ist.


    Zusammengefasst: AutoIt analysiert die Struktur, erkennt ein dword und muss daher davon ausgehen, dass eine Zahl mit dem Binärwert 0xFFFFFFFF als unsigned long interpretiert werden muss und daher dem Gegenwert 4.294.967.295 entspricht. Es hat keine Möglichkeit von den Spielereien in C zu wissen wo auch ein unsigned long gegen negative Zahlen getestet werden kann.


    Ich kann daher kein wirkliches Fehl- oder Spezialverhalten von AutoIt in diesem Beispiel erkennen. AutoIt macht im Grunde alles richtig. (Edit: Bis auf den Umstand, dass in der Hilfe -1 als möglicher Rückgabewert angegeben wird - was defacto nicht möglich ist)
    Das kann man auch schön daran erkennen, dass auch in C 4.294.967.295 herauskommt wenn man sich ordentlich an die Doku hält:


    Edit: Hab mal >>ein Ticket<< dazu aufgemacht, so dass die Funktion auch -1 zurückgeben würde.

    Edit2: Gefixt und soll in 3.3.15.5 behoben sein

    Zudem würde ich deine Schleife anders aufbauen:

    _FileCountLines nutzen anstatt While und @error

    1. Dann geht er die Datei 2x mal durch anstatt wie jetzt nur einmal. Dazu hat er während des _FileCountLines die gesamte Datei schon im Speicher und schmeißt sie dann wieder raus um sie anschließend zeilenweise wieder durchzuarbeiten. Ressourceneffizient geht anders.
    2. Wozu muss man vor der Schleife die Anzahl der Zeilen zu wissen? Ich hoffe nicht um mit dieser den line-Parameter von FileReadLine zu befüllen - dann wird es nämlich erst richtig lahm.

    Wenn man eine Datei wirklich zeilenweise durchgehen will ohne die gesamte Datei im Speicher vorzuhalten ist der Grundaufbau von Sascha meiner Meinung nach schon die grundsätzlich vernünftigste Methode.

    1. Wenn ich ein mit AutoIt selbst erstelltes und kompiliertes Programm veröffentlichen will, habe ich da in Bezug auf Lizenz-Rechte, etc. seitens AutoIt etwas zu beachten?

    Hierzu ein Zitat aus den Lizenzbedingungen von AutoIt - sollte selbsterklärend sein:

    Zitat

    Commercial Use. You may use the SOFTWARE PRODUCT for commercial purposes. You may sell for profit and freely distribute scripts and/or compiled scripts that were created with the SOFTWARE PRODUCT.

    Ja man kann natürlich auch eine Variable zeilenweise durchgehen.

    Es kommen zum Fall hier für mich jedoch einige Fragen zusammen:

    • Warum soll zeilenweise gearbeitet werden und warum reicht hier nicht soetwas aus?
    AutoIt
    $sNewFile = StringReplace(FileRead("test.txt"), ".", "")
    FileDelete("test.txt")
    FileWrite("test.txt", $sNewFile)
    • Welche Verbesserung erhoffst du dir durch die Umstellung auf die Bearbeitung der Variable anstatt der Datei?
    • So wie ich deine FileVariante verstehe, dürfte diese nicht funktionieren, da 1. die Datei im Overwrite-Modus geöffnet wird und damit die Datei noch vor dem Einlesen geleert wird und 2. werden die veränderten Zeilen nur an das Ende drangehangen anstatt, dass die vorherigen überschrieben werden.

    Da mir konkrete Problemstellung und Seitenbedingungen fehlen, weiß ich nicht ob folgendes deine Frage beantwortet:

    AutoIt
    Global $sFile = ""
    For $sLine In FileReadToArray("test.txt") 
        $sFile &= StringReplace($sLine, ".", "") & @CRLF
    Next
    $sFile = StringTrimRight($sFile, 2) ; das letzte CRLF wieder entfernen
    
    ; Der bearbeitete Variableninhalt:
    ConsoleWrite($sFile)

    Also: Das Problem ist nicht die Anzahl der Spalten (hätte mich auch gewundert).


    Das Problem ist stattdessen die Anzahl der Zeilen.

    Inhaltlich sind nur 344 Zeilen beschrieben.
    Definiert (z.B. durch Zuweisung einer Formatierung oder ähnlichem) sind hingegen satte 1.046.932 Zeilen.

    Und die stehen alle Zeile für Zeile, Zelle für Zelle in der Datei drin.

    Das hat zur Folge, dass die xml für das Worksheet 108 MB groß ist.
    Und hier streikt dann direkt der XML-Parser.

    Daher bringt es gar nichts wenn ich noch eine Beschränkung für die Zeilen mit aufnehme.


    Die Lösung ist aber entsprechend einfach: Alle Zeilen ab der 345. löschen (entsprechende Datei hier im Anhang).
    Und dem Kollegen auf die Finger klopfen der offensichtlich eiskalt bis zum Ende runtergescrollt hat und aus lauter Blödheit einfach mal sinnfrei Zellen formatiert hat.

    Ok includes mit "" sind immer im lokalen Ordner de Programms und eigene (fremde) AU3.

    Nicht ganz. Die Frage ob man <...> oder "..." nimmt hat lediglich Einfluss auf die Suchreihenfolge in welchen Ordnern nach der jeweiligen UDF gesucht wird.

    Bei <...> wird erst im AutoIt-Include-Verzeichnis geschaut, dann optional in einem selbstfestgelegtem, welchen man in der Registry eintragen kann und erst wenn dort nichts gefunden wurde wird im Skriptverzeichnis geschaut.

    Bei "..." ist es genau andersherum: Es wird also erst im Skriptverzeichnis geschaut und erst am Schluss im AutoIt-Include-Verzeichnis.

    Es ist also durchaus möglich die AutoIt-internen UDFs mit Anführungszeichen zu includen.

    Ja klar könnte ich das mit implementieren.
    Wird aber ne zeitlang dauern.
    Komme wahrscheinlich erst im September wieder dazu.


    Prinzipiell sind mehr Spalten nicht unbedingt ein Problem.
    Es kommt auf das interne Format an. Es gibt die Möglichkeit jede Zeile einzeln mit ihrer Koordinate einzutragen.
    Das ist gut bei dünn besetzten Tabellen - also vielen Lücken zwischen den Zellen.

    Oder die Zellen werden zeilenweise eingetragen egal ob da was drin steht oder nicht.


    Ansonsten schick mir mal die Datei - anhand solcher Beispiele findet man oftmals noch Optimierungsmöglichkeiten.

    Weil ich neugierig war ob der "ich nehme einfach unmengen For-Loops und lehne mich zurück" Ansatz klappt habe ich es kurz implementiert... Entweder ist da was falsch, oder es gibt wesentlich weniger gültige Permutationen als ich gedacht habe... Bei so viel Zahlensalat habe ich mich bestimmt irgendwo vertippt

    Ich habe mal einen unabhängigen Ansatz versucht und komme auf das gleiche Ergebnis wie du.
    Sollte also wahrscheinlich so stimmen:


    Also auf die Kern-Frage, wie ich die Beschreibung der Formation am Besten abspeichere, ist dein Vorschlag eine Matrix für jede Position in dieser Formation in einem 2D-Array anzulegen und dann jeweils bei den Kanten zu sagen, ob sie "verbunden" sind?

    Vllt. habe ich es auch noch nicht ganz durchschaut, aber das kommt mir sehr kompliziert und rechenaufwendig vor.

    Die relevanten Fachbegriffe für die Google-Suche hierzu sind Adjazenzmatrix für die Abbildung als Matrix wie von Mars vorgeschlagen und als Alternative hierzu die Adjazenzliste.
    Welche Struktur man für die Knotenbeziehung in Graphen verwendet hängt hierbei vor allem davon ab ob eine Adjazenzmatrix dünn oder dicht besetzt wäre. In dem Fall hier würde ich wohl eher zu einer Adjazenzliste neigen, da jeder Knoten ja nur max. 2 Kanten haben kann (hab ich das so richtig verstanden? ne jetzt sehe ich in deinem Bild, dass es auch mehr Verknüpfungen sein können). Außerdem hat man in einer Adjazenzliste sofort alle Nachbarknoten vorliegen, während man in einer Adjazenzmatrix die komplette Spalte jeweils durchgehen müsste.

    Ich kann das Verhalten bei mir mit Prozessen nachstellen, welche mit einem höheren Benutzer gestartet wurden.
    Ich denke daher es hat irgendetwas mit den Rechten zu tun.

    Versuche daher einfach mal folgenden Workaround:

    AutoIt
    While ProcessExists("StarMoney.exe")
        Sleep(250)
    WEnd

    Du hast sogar mit eingebaut, dass er möglichst die Lösung mit den wenigsten Gutscheinen (Anzahl) verwenden soll.

    Das ist das einzige was nicht funktioniert hat.


    Ich meine den Fehler gefunden zu haben:

    Zeile 61 müsste meiner Meinung nach heißen: If ($nRabatt = $nRabattMax) and ($iRabattMaxN <= UBound($aIndGroup)) Then ContinueLoop

    Super gesehen. Genau dort liegt ein Fehler.

    Du hast definitiv das Skript verstanden :thumbup:

    Kannst Du das Pattern so umschreiben, dass es unabhängig von einer Topleveldomain ist?

    Laut Wiki gibt es ca. 1500 Topleveldomains.

    Es sind sehr viele unterschiedliche Topleveldomains.

    Ja klar ist das möglich aber im Zweifel wackliger.
    Warum sieht man schön anhand deines Ausschnittes wo die Datei plusone.js genannt wird.
    Diese stellt von ihrer Form allein, so wie sie ist, eine gültige Domain dar.

    Sie ist es jedoch bekanntlich nicht und soll auch nicht gematcht werden, aber das ergibt sich für uns nicht aus deren Aufbau, sondern erst aus dem Kontext in welchem sie steht.

    Es gibt daher zwei Möglichkeiten:

    1. Da .js keine gültige TLD ist, kann man (wie bisher) die TLDs auf eine bestimmte Liste eingrenzen. Das sind ca. 1500 und man kann sich das Pattern immer topaktuell direkt von AutoIt damit zusammenbauen lassen. Wie Musashi aber bereits schon anmerkte ist das Performance-mäßig nicht ganz die Wunschoption.
    2. Man fügt Kontext-Informationen hinzu. Also was darf vor der Domain stehen, was dahinter?
      Das hast du in deinem Vorschlag ja bereits getan wo du "gefolgt von / bzw. ##" vorgeschlagen hast.

    Nr. 2 habe ich mal umgesetzt: https://regex101.com/r/h4AaCM/6

    Warum zwei getrennte Abfragen?

    Mal schnell und etwas naiv rangegangen - aber so sollte das Prinzip klar bleiben und kann auch leicht angepasst werden.:


    regex101: build, test, and debug regex
    Regular expression tester with syntax highlighting, explanation, cheat sheet for PHP/PCRE, Python, GO, JavaScript, Java. Features a regex quiz & library.
    regex101.com


    Was meine ich mit "naiv"?:

    • ist aktuell auf feste Top-Level-Domains beschränkt (die du gerne erweitern kannst oder durch etwas variables ersetzen)
    • Umlaute und diakritische Zeichen sind prinzipiell auch (in Abhängigkeit von der Top-Level-Domain) erlaubt (Die IDN-Tabellen) - hier aber nicht.

    Die Frage ob eine Datenbank wie SQLite hier sinnvoll wäre oder nicht können wir anhand des gezeigten gar nicht beurteilen.
    Für eine Datenbank müssen die Datensätze eine in sich wohnende feste Struktur haben.

    Das gezeigte Beispiel ist jedoch mal wieder herrlich unkonkret was komplett verhindert ableiten zu können wie man mit diesen Daten am besten umgeht.
    Da stehen nur durchnummerierte Datensatz-Header mit darauf folgenden variablen Anzahl an Textzeilen.
    Die Frage ist doch aber gibt es dort irgendeine inhaltliche Struktur? Gibt es dort eventuell bestimmte Attribute die jeden Datensatz beschreiben?

    Mit solchen nonsens-Beispielen kann man schlicht nicht adäquat Hinweise geben wie man damit umgeht.


    Zu deinem _ArrayDelete: Ja ist langsam weil bei einem einzelnen ArrayDelete alle Elemente die danach kommen einzeln nach vorne im Array verschoben werden müssen.
    Das dauert.
    Eventuell kann man versuchen mehrere Löschungen in einen Durchlauf unterzubringen so dass man nur einen Durchlauf braucht.
    Aber auch hier: Bitte ganz konkret am tatsächlichen Sachverhalt.


    Edit: Mal als ganz unkonkretes Beispiel wie ich das mit dem Delete in einem Durchlauf meinte: