Ja Run() in Verbindung mit @Comspec.
Beiträge von AspirinJunkie
-
-
Schau mal meinen Edit oben bezüglich der AutoIt-Version an.
Die JSON_Dictionary wird im übrigen seit einiger Zeit nicht mehr gepflegt.
Da du aber eh Maps verwenden wolltest, verstehe ich nicht, warum du diese anstatt der JSON.au3 nimmst. -
Ich kann dein Problem nicht reproduzieren.
Die angehangene Datei "Rubber.json" wird bei mir in 1,6s eingelesen.
Um das offensichtlich bei dir vorhandene Problem zu ergründen benötigen wir nun ein Minimalskript, welches das von dir beschriebene Verhalten reproduziert (nein - das bisher gepostete ist kein Minimalskript - es sind nur 2 Funktionen), sowie die tatsächlichen Daten mit der das Problem auftritt.
Dass du die aktuelle Version der UDF verwendest, setze ich mal als gegeben voraus.
Btw: In der JSON-Datei meine ich ein Problem mit den Datentypen zu sehen: Offensichtliche numerische Werte wie 3.245038e+01 sind in der JSON-Datei als Stringtyp hinterlegt (in Anführungszeichen).
Da die _JSON_Generate() nicht einfach aus numerischen Typen Strings bastelt, muss der falsche Datentyp bereits in den Ausgangsdaten vorhanden sein.
Edit: Oder noch viel wichtiger: Welche Version von AutoIt verwendest du?
In Version 3.16.0 wurde ein Bug in StringRegExp gefixt, welcher insbesondere Mehrfachaufrufe von großen Strings enorm ausgebremst hat.
Das war insbesondere für die UDF hier tödlich.
Heißt: Mit allen älteren Versionen sollte das von dir beschriebene Verhalten auftauchen. -
Wenn ich jetzt meine Arbeitsstunden vom Dezember mit der Abrechung vergleiche steht 103,99 zu 104 da. Ist wohl der Rundung geschuldet.
Rundungsfehler? Hm - zeig mal dein Skript.
Im Grunde kann man das nahezu vollständig auf Integerbasis aufbauen - da sollte es nicht zu derartigen Rundungsfehlern kommen. -
-
Vielleicht ist es etwas einfacher zu rechnen, wenn du genau andersherum rangehst.
Anstatt Minuten in Stunden umzurechnen um sie dann zu addieren, sollte es einfacher sein eine Summe aller Minuten $nMinuten zu berechnen.
Das ist einfach, da man nur die Minuten addieren braucht und die Stunden sind ja nur noch mit 60 zu multiplizieren.Dann ist die Rechnung am Ende ziemlich easy:
-
Nun du hast folgende Teilaufgaben vor dir:
- Eine Liste aller Ordner erstellen: _FileListToArray würde hierfür gehen aber auch mit den zugrunde liegenden Funktion FileFindFirst/NextFile könnte man dies machen.
- Die Liste in einer Schleife durchlaufen
- Für jeden Ordner den Erstellzeitpunkt ermitteln: FileGetTime
- Die Differenz zu jetzt bestimmen: Entweder manuell über die einzelnen Zeitbestandteile oder mit Hilfe der Funktion _DateDiff - dort müsstest du dir aber noch den Zeitstring aus FileGetTime entsprechend formatieren.
- Falls die Differenz ein bestimmte Maß überschreitet - Ordner löschen.
-
In dem beigefügten Code-Schnipsel müsste eigentlich $B = $A sein, aber die Quelle $A ist ein 2D Array, und B besteht scheinbar aus 2x 1D Array, bzw. _ArrayDisplay kann $B nicht richtig auflösen/ darstellen.
Exakt. JSON, als auch CBOR hat keine Möglichkeit mehrdimensionale Arrays von verschachtelten Arrays zu unterscheiden.
Dies hängt damit zusammen, dass viele Programmiersprachen mehrdimensionale Arrays in dieser Form gar nicht kennen (Javascript oder Python wären Beispiele hierfür).
Ein 2D-Array wird dort also implizit in ein Array-In-Array serialisiert.
Beim Einlesen wiederrum, gibt es keine Möglichkeit zu detektieren ob es sich mal um ein mehrdimensionales Array oder ein Array-In-Array handeln soll.
Daher obliegt es dem User die Arrays bei Bedarf in die Form zu bringen, welche er benötigt.
Genau daher gibt es in der JSON-UDF Funktionen, welche genau dies bewerkstelligen, so dass dein Skript folgendermaßen anzupassen wäre:AutoIt
Alles anzeigen#include <JSON.au3> #include <Array.au3> ; Aufbau der Datenstruktur in AutoIt Global $A[2][2] = [[1, 2], [3, 4]] Global $M[] $M["A"] = $A $M["S"] = "Hallo Welt" Global $M1 = $M $M["M"] = $M1 ; erstmal löschen - sonst wird es nur angefügt FileDelete("mtest.json") ; AutoIt-Variable in JSON-Datei serialisieren FileWrite("mtest.json", _JSON_GenerateCompact($M)) ; JSON-Datei wieder in AutoIt deserialisieren: Global $InPut = FileRead("mtest.json") Global $M2 = _JSON_Parse($InPut) ; Array-In-Array in ein 2D-Array konvertieren: $B = __JSON_AinAToA2d($M2["A"]) ; Ausgeben _ArrayDisplay($A, "$A") _ArrayDisplay($B, "$B")
-
Der beschriebene Weg ist wahrscheinlich der von Profis!
[...]
Ich denke jetzt evtl. zu naiv, und weil ich keine Schnittstelle zu anderen Sprachen benötige, dass man die Map im Speicher lokalisieren könnte, und diese Daten dann in einem binären Format abspeichert, und später wieder einlesen kann?So richtig weiß ich ehrlich gesagt nicht was du denkst.
Du wolltest eine Funktionialität mit welcher du verschachtelte AutoIt-Strukturen in Dateien abspeichern kannst und später wieder einlesen.
Sowohl CBOR als auch JSON machen das mit dem nur denkbar geringsten Aufwand.
Beispiel JSON:AutoIt; AutoIt-Variable in Datei speichern FileWrite("myData.json", _JSON_GenerateCompact($mMap)) ; AutoIt-Struktur aus JSON-formatierter Datei einlesen: $mMap = _JSON_Parse(FileRead("myData.json"))
Beispiel CBOR:
AutoIt; AutoIt-Variable in Datei speichern FileWrite("myData.bin", _cbor_encode($mMap)) ; AutoIt-Struktur aus CBOR-formatierter Datei einlesen: $mMap = _cbor_decode(FileRead("myData.bin"))
Ich weiß nicht was dich daran stört.
Das Label "Profi" verstehe ich auch nicht in dem Zusammenhang.
Mal abgesehen davon, dass ich genauso wenig Profi bin wie du (vermutlich), sollte die Auswahl der Werkzeuge sich bei einem Amateur nicht wirklich von einem Profi unterscheiden.
In beiden Fällen wird das Werkzeug genommen mit der man eine gestellte Aufgabe möglichst effektiv und effizient lösen kann.
Die Anforderungen bei einem Profi sind es, welche eine Unterscheidung bringen können.
Aber bei gleicher Aufgabenstellung sollten auch ähnliche Lösungen herauskommen.
Du möchtest den vorgeschlagenen Ansatz nun nicht verwenden weil sie deiner Meinung nach "Profis" vorbehalten ist? - hab ich das so richtig verstanden?
Was möchtest du dann?
Eine umständlichere, fehleranfälligere Lösung damit sie mehr nach Amateur aussieht?
JSON und CBOR darfst du als "Amateur" genauso verwenden ohne dass jemand dann mit dir schimpft.
Wenn ich dir stattdessen zeigen wöllte was für ein toller Hecht ich doch bin, dann hätte ich dir einfach die Begriffe Avro, Thrift oder protobuf vor die Füße geworfen und mich dann als echt großer Profi gefühlt...
Das hätte dich kein Stück weiter gebracht - im Gegensatz zu meinem Vorschlag mit JSON und CBOR.
Das war tasächlich ernst gemeint von mir, dass ich dies für die sinnvollste Lösung für deine Problemstellung halte.
Aber behalte im Hinterkopf: Es ist nur eine Amateurmeinung - Profis würden dir vielleicht zu etwas ganz anderem raten... -
Ja - der Begriff hierzu heißt "Serialisieren/Deserialisieren".
Man nimmt eine Datenstruktur in einer Programmiersprache und speichert diese als Datei so ab, dass die Hierarchie und die Datentypen erhalten bleiben.
Anschließend kann man diese Datei wieder irgendwann einlesen und erhält genau diese Datenstruktur wieder in seinem Programm.
Bei den Formaten bist du ziemlich frei. Du kannst dir was völlig eigenes überlegen oder vorhandene Formate hierfür verwenden.
Ini z.B. ist halt beschränkt, da dort Verschachtelungen nicht gut umsetzbar sind aber wenn deine Datenhierarchie flach ist, kann ini ein guter Kandidat hierfür sein.
Für beliebig verschachtelte Datenstrukturen käme z.B. XML in Frage aber da ist mir keine UDF bekannt, welche direkt aus AutoIt-Strukturen lesen und in diese schreiben kann.
Da müsstest du also was eigenes drumherum basteln.
Bleiben noch die klassischen Kandidaten für diese Aufgabe: >>JSON<< - als (menschenlesbares) Textformat oder >>CBOR<< als (speicherkompaktes) Binärformat.
Das schöne an solchen standardisierten Formaten ist es, dass man problemlos zwischen Programmen die Daten austauschen kann.
Also eine JSON oder CBOR-Datei, welche aus AutoIt heraus erzeugt wurde, kann dann wieder in Python eingelesen werden und dort weitergearbeitet werden. -
Das ist weniger eine Frage von ShellExecute als mehr von dem Programm was am Ende ausgeführt wird.
ShellExecute schaut lediglich in der Registry nach mit welchem Programm eine Dateiendung verknüpft ist wie diese aufzurufen ist (Siehe Zweig HKEY_CLASSES_ROOT).
Im Falle vom Acrobat sollte (hab kein Acrobat) sowas in der Art stehen: "C:\...\acrobat.exe" "%1"Sprich: Der Acrobat-Reader wird gestartet und bekommt den Dateinamen als Parameter übergeben.
In deinem Fall würde im Dateinamen noch ein Sternchen stehen und nun kommt es also darauf an ob der Acrobat Reader mit Sternchen als Wildcards umgehen kann oder eben nicht.
Offensichtlich kann es der Acrobat Reader ja nicht.
Also musst du dir anders behelfen und da gibt es die Möglichkeit dem Reader mehrere Dateinamen zu übergeben.
Also wie Moombas sagte die Dateiliste erzeugen und als mehrere Parameter dem Acrobat Reader überreichen. -
aber was geschieht wenn man in meinem beispiel 2mal homepage vorkommt?
Dann kannst du _td_toPrimaryKeys() halt einfach nicht nutzen, da Primärschlüssel nunmal eindeutig sein müssen.
wie geht man dann mit den daten um?
So wie du es brauchst und es sinnvoll ist.
Du kannst die Daten als 2D-Array behandeln, wenn du $mData.Data nimmst.
Du kannst einzelne oder mehrere Spalten per Name extrahieren wenn du _td_getColumns() verwendest.
Du kannst Prinzipiell spaltenorientiert arbeiten, wenn du _td_toColumns() nimmst.
Du kannst die Datensätze mit _td_toObjects() als Liste von Objekten behandeln, so dass du auf deren Attribute einfach per Name zugreifst.
Du kannst die Daten anzeigen lassen (_td_Display), als csv-formatierten String ausgeben (_td_toCsv), als fixed-width-String ausgeben (_td_toFixWidth) oder als Array ausgeben (_td_toArray).
Es kommt halt ganz auf den Anwendungsfall an.
Zu allen Funktionen habe ich im example-Ordner Beispiele und die jeweiligen Funktionsheader sollten auch hilfreich sein. -
Ja das ist das Konzept der UDF.
Die Daten werden vom Header getrennt behandelt.
Auf die Art kann man die Spalten und Attribute über die Namen ansprechen.
Das bedingt aber halt, dass Headerinformationen da sind.Ein Blick in die Funktionsbeschreibung zeigt auf welche Arten man diese Header-Informationen per $vHeader-Parameter erhalten kann:
- False: Es gibt keine Headerinfos - der User kann selbst im Nachgang diese hinzufügen, wenn er $mTable.Header mit einem entsprechenden 1D-Array beschreibt
- True: Die erste Zeile enthält den Header
- Array: Der User setzt eigene Headerelemente per Array
- String: Der User setzt eigene Headerelemente per String (getrennt durch $sDelim - meistens "|")
-
AutoIt
Alles anzeigen#include "TableData.au3" ; Transfer data to table type: $mData = _td_fromCsv("Test.csv", ',', "", True) ; display data _td_display($mData, "MojoB`s CSV") ; Zugriff per Unique-Key $mTable = _td_toPrimaryKeys($mData) MsgBox(0, "homepage", $mTable.homepage.WERT)
Du hast beim Einlesen mit _td_fromCsv() angegeben, dass in der CSV-Datei keine Header-Zeile vorhanden ist (4. Parameter = False).
Die Spalten haben also keinen zugeordneten Namen, stattdessen wurde die Header-Zeile als normale Datenzeile behandelt.
Da die Spalten keinen Namen haben kann auch _td_toPrimaryKeys nichts mit diesen Bezeichnungen anfangen. -
-
OK ich habe die Benamung der "-to"-Funktionen jetzt komplett umgestellt und auch die vorgeschlagene Funktion _td_toPrimaryKeys() mit aufgenommen (Danke Moombas für die gute Idee).
Wer für die Funktion evtl. einen schmissigeren Namen hat - gerne her damit - ich bin noch nicht ganz zufrieden damit.
Außerdem ist noch eine kleine Funktion _td_toArray dazugekommen. -
Hm ne würde ich ungern machen.
Neben der weiter anwachsenden Parameteranzahl würde ich auch ein paar "Prinzipien" in der UDF durcheinander hauen.
Die _td_fromXXX-Funktionen haben gemeinsam, dass Sie alle das Table-Objekt mit .data und .header zurückgeben.
Funktionen, welche diese dann je in andere Strukturen umwandeln, kommen dann danach als zweiter Schritt.
Also die Funktionen _td_toColumns, _td_getColumn, _td_TableToMaps.
Diese Funktion hier würde sich vom Ansatz her in letztere einordnen.
Bei der Gelegenheit fällt mir aber auf, dass die Funktionsnamen noch nicht optimal sind.
Wenn ich auf der einen Seite "-from"-Funktionen habe, dann sollten die anderen konsequent "-to"-Funktionen heißen.
Das werde ich wohl sicher so noch mal überarbeiten. -
Es soll nur an Tagen, die in folge das gleiche Datum haben, wie im Beispiel 05.10. 4x oder 11.10. 3x geschehen.
Schnell umgesetzen könnte man das so:
AutoIt
Alles anzeigen#include <Array.au3> Global Const $nTAGESGRENZE = 8 Global $aDate[] = ["01.10", "02.10", "03.10", "04.10", "05.10", "05.10", "05.10", "05.10", "06.10", "07.10", "08.10", "09.10", "10.10", "11.10", "11.10", "11.10"] Global $aValue[] = [8, 10, 8, 6, 2, 4, 10, 2, 6, 6, 6, 6, 6, 2, 8, 2] ; aufpassen: hier hattest du die Werte als Strings - es sollten aber idealerweise Zahlentypen sein Global $aResultValue[UBound($aDate)] Global $sDatum = "", $nWert, $nSumme, $iPos = 0, $iRest = 0 For $i = 0 To UBound($aDate) - 1 $nWert = $aValue[$i] ; Datumswechsel behandeln If $aDate[$i] <> $sDatum Then $sDatum = $aDate[$i] $nSumme = 0 $iPos += $iRest $iRest = 0 EndIf If $nSumme < $nTAGESGRENZE Then ; solange Grenze noch nicht erreicht wird die Werte dem Zielarray hinzufügen If $nSumme = 0 Or ($nSumme + $nWert <= $nTAGESGRENZE) Then $aResultValue[$iPos] = $nWert > $nTAGESGRENZE ? $nTAGESGRENZE : $nWert Else $aResultValue[$iPos] = $nTAGESGRENZE - $nSumme EndIf $nSumme += $nWert $iPos += 1 Else ; Falls Grenze bereits erreicht nichts machen und die Leerstellen zählen $iRest += 1 EndIf Next ; Ausgabe in ein gemeinsames Array um besser zu sehen Local $aData[UBound($aDate)][3] For $i = 0 To UBound($aDate) - 1 $aData[$i][0] = $aDate[$i] $aData[$i][1] = $aValue[$i] $aData[$i][2] = $aResultValue[$i] Next _ArrayDisplay($aData, "", "", 64, "|", "Datum|Ausgangswerte|Zielwerte")
-
Interessanter Ansatz.
Meinst du das in der Art?:AutoIt
Alles anzeigen#include "TableData.au3" ; the origin data Global $aArray[5][4] = [["name", "age", "salary", "married"], ["Max", 25, 5000.50, True], ["Anna", 30, 6000.75, False], ["Peter", 35, 7000.25, True], ["Lena", 28, 5500.50, False]] ; convert into a table object: $mData = _td_fromArray($aArray, True) ; convert into primary key focused data structure: $mTable = _td_dataByPrimaryKey($mData) ; access to the data by primary key MsgBox(0, "Anna`s salary", $mTable.Anna.salary) ; #FUNCTION# ====================================================================================== ; Name ..........: _td_dataByPrimaryKey() ; Description ...: converts a table object into a map in which the data is stored with its primary key ; Syntax ........: _td_dataByPrimaryKey(ByRef $mTable, $vPrimaryColumn = 0) ; Parameters ....: $mTable - table object structured like in this udf ; $vPrimaryColumn - column identifier (integer index or column name as string) ; which holds the unique primary key of the data ; Return values .: Success: a AutoIt-Map where Keys=Primary Keys and value=map of data ; Failure: Null ; @error = 1: wrong format of $mTable ; @error = 2: not enough header elements ; @error = 3: wrong datype of $vPrimaryColumn (only string or int) ; @error = 4: column name in $vPrimaryColumn is not contained in the header ; @error = 5: wrong column index in $vPrimaryColumn ; @error = 6: values in the column $vPrimaryColumn are not unique ; Author ........: AspirinJunkie ; Last changed ..: 2024-01-18 ; ================================================================================================= Func _td_dataByPrimaryKey(ByRef $mTable, $vPrimaryColumn = 0) If Not IsMap($mTable) Or Not MapExists($mTable, "Header") Or Not MapExists($mTable, "Data") Then Return SetError(1,0,Null) Local $aHeader = $mTable.Header Local $aData = $mTable.Data If UBound($aHeader) < UBound($aData, 2) Then Return SetError(2, UBound($aHeader), Null) ; check for validity of $vPrimaryColumn If IsString($vPrimaryColumn) Then $vPrimaryColumn = _ArraySearch($aHeader, $vPrimaryColumn) If $vPrimaryColumn < 0 Then Return SetError(4, @error, Null) ElseIf IsInt($vPrimaryColumn) Then If $vPrimaryColumn < 0 Or $vPrimaryColumn >= UBound($aData, 2) Then Return SetError(5, UBound($aData, 2), Null) Else Return SetError(3, 0, Null) EndIf ; transfer the data into the primary-key focused map-structure Local $mRet[] For $i = 0 To UBound($aData, 1) - 1 ; check if key is really unique If MapExists($mRet, $aData[$i][$vPrimaryColumn]) Then Return SetError(6, 0, Null) Local $mEntry[] For $j = 0 To UBound($aData, 2) - 1 $mEntry[$aHeader[$j]] = $aData[$i][$j] Next $mRet[$aData[$i][$vPrimaryColumn]] = $mEntry Next Return $mRet EndFunc
-