Wie führt man ein Listenobjekt, das in einer TXT-Datei gespeichert ist, in ein AppleScript ein?

Ich habe eine AppleScript .scpt-Datei, die durch eine Tastenkombination in FastScripts.app ausgelöst wird und als Thesaurus fungiert. Das Skript sucht das ausgewählte Wort in einer vorformatierten Liste, und wenn das Wort in dieser Liste gefunden wird, zeigt es dem Benutzer die Synonyme dieses Wortes an 1 .

Diese Liste ist in einer Nur-Text-Datei (.txt) enthalten. Die Liste ist bereits im AppleScript- listFormat formatiert. Ich möchte, dass meine .scpt-Datei diesen Text als echte Liste akzeptieren kann 2 .

Es ist wichtig zu beachten, dass die .txt-Datei 2,5 Millionen Wörter enthält 3 .

Aus diesem Grund kopiere ich nicht einfach den Inhalt der .txt-Datei in die .scpt-Datei selbst, obwohl die Textdatei zu 100 % statisch ist und niemals geändert wird. Das direkte Einfügen des Textes in mein Skript würde erhebliche Verzögerungen und Trägheit mit sich bringen, wenn ich meine .scpt-Datei in Script Editor.app bearbeite und kompiliere.

Script Editor.app fror jedes Mal ein, wenn ich versuchte, die .txt-Datei zu lesen. Das Problem besteht darin, dass der Skripteditor eine bestimmte Textdatei vollständig in den Speicher liest, anstatt den Inhalt effizienter zu streamen. Also habe ich diese Textdatei in 10 kleinere Textdateien 4 aufgeteilt , wobei jede neue .txt-Datei etwa 250.000 Wörter enthält.

Mit 250.000 Wörtern sind die Textdateien natürlich immer noch extrem groß (nach jedem Standard).

Hier ist ein (stark komprimiertes) Beispiel dafür, wie der Inhalt jeder Textdatei aussieht:

{{"Erhöhung","Auszeichnung","Schmetterling","Fortschritt","Fortschritt"},{"erhaben","Worfeln","winsome"},{"Prüfung","Vorsprechen","Blaues Buch" ,"examen","examination","final","examination","test","trial","tripos","viva","schriftliche","schriftliche klausur"},{"examen","Pap test","sokratische methode","lüften","analyse","anatomische diagnostik","beurteilung","aufarbeitung","schriftlich","schriftliche prüfung"},{"untersuchen","luft", "analysieren","schätzen","archetyp","schlafen","bewerten","canvas","fall"},{"prüfer","analytiker","analysator","fragesteller"},{"untersuchen","analytisch","prüfen","explorativ"},{"beispiel","ermahnung"," ermahnung","alarm","archetyp"},{"verärgern","beteufel","ärgern","aufarbeiten","sorgen"},{"verärgert","verschärft","verstärkt","wütend ","genervt"},{"ärgerlich","ärgerlich","lästig","lästig"}}alarm","archetyp"},{"verärgern","beteufel","verärgern","aufarbeiten","sorgen"},{"verärgert","verärgert","verstärkt","wütend","genervt "},{"ärgerlich","nervig","lästig","lästig"}}alarm","archetyp"},{"verärgern","beteufel","verärgern","aufarbeiten","sorgen"},{"verärgert","verärgert","verstärkt","wütend","genervt "},{"ärgerlich","nervig","lästig","lästig"}}

Wie Sie sehen können, ist der Inhalt der Textdatei eine verschachtelte Liste 5 , die genauso organisiert ist wie AppleScript eine list. Jede Textdatei enthält keine Zeilenumbrüche oder Absätze.

Ich suche nach einer Methode, um diese Liste mit möglichst wenig Latenz in mein AppleScript zu bekommen 6 . Deshalb habe ich es vorformatiert. Geschwindigkeit ist also entscheidend .


Fußnoten:

1. Mein Thesaurus-Skript ähnelt der integrierten Thesaurus-Funktion, die in Microsoft Word vorhanden ist. Ein bemerkenswerter Unterschied besteht darin, dass mein Skript systemweit funktioniert.

2. Mit echter Liste meine ich, dass ich zB item 12diese Liste später in meinem AppleScript aufrufen kann.

3. Meine Quelle für die Thesaurusdaten ist der „Moby“-Thesaurus von Grady Ward. Ich habe diese Datenbank aus dieser Antwort gefunden: Auf der Suche nach Thesaurus-Daten - Stapelüberlauf

4. Ich musste Hex Fiend.app verwenden, um aus der Textdatei auszuschneiden und in eine neue Textdatei einzufügen. Ich konnte die Datei in TextEdit.app nicht bearbeiten, ohne dass TextEdit bei mir einfriert.

5. Die äußere Liste enthält jeden Thesauruseintrag. Die inneren Listen enthalten alle Synonyme für diesen Eintrag. Das erste Element jeder inneren Liste ist der Eintragstitel. Sowohl die äußere Liste als auch jede innere Liste sind alphabetisch geordnet (mit Ausnahme des ersten Wortes jeder inneren Liste, da dieses Wort wiederum der Eintragstitel ist).

6. Mir ist bewusst, dass selbst die schnellste Methode noch einige Sekunden Latenz hat, da die Textdatei so groß ist.


Warum verwendest du keine SQLite-Datenbank?
Ich hatte noch nie von SQLite gehört, bis Sie es erwähnt haben. Ich kenne mich damit absolut nicht aus. Wenn Sie jedoch wissen, wie ich damit erreichen kann, was ich will, bin ich gerne bereit, alles zu verwenden.
Wozu dienen die 2,5 MWörter? Die Idee hinter meinem Vorschlag ist, eine Datenbank anstelle einer im Grunde flachen Textdatei zu verwenden.
Bitte beachten Sie meine letzte Bearbeitung. Ich habe weitere Einzelheiten zu meinem Skript angegeben.

Antworten (2)

Offensichtlich kenne ich nicht den Gesamtumfang dessen, was Sie tun oder wie Sie andere Dinge codiert haben, da Sie nicht alle Details und Codes angegeben haben, aber ich würde einen anderen Ansatz wählen.

Ich habe den Moby Thesaurus von der verlinkten Seite in Ihrer Frage heruntergeladen und die folgenden Aktionen darauf ausgeführt.

  1. Inhalt der mthes.tar.ZDatei extrahiert.
  2. Ich habe die ./mthes/mobythes.aurDatei in TextWrangler geöffnet und festgestellt, dass sich zwei Dinge ändern.
    • Ändern Sie die Zeilenenden von Classic Mac (CR) in Unix (LF).
    • Unerwünschte abschließende Kommas aus 6 Zeilen entfernt.

Beachten Sie, dass ich diese Änderungen zwar in TextWrangler vornehmen könnte, ich es aber dennoch vorziehe, Terminal zu verwenden, und dies mit dem folgenden Befehl:

tr "\r" "\n" < mobythes.aur | sed -E 's/[,]{1,}$//' > mobythes.txt

Was nur buchstäblich eine Sekunde dauerte (da ich dem obigen Befehl tatsächlich timeaus Neugierde vorangestellt habe). Nachdem die mobythes.aurDatei nun verarbeitet, gespeichert mobythes.txtund in meinen Ordner „Dokumente“ kopiert wurde, werde ich diese neue einfache CSV-Datei so verwenden, wie sie ist, um die Suchzeichenfolge nach einer Übereinstimmung mit dem ersten Feld jedes Datensatzes abzufragen und den Datensatz ohne den zurückzugeben erste Feld als Liste zur Auswahl in AppleScript. Ich fand diese Methode extrem schnell, während ich beim Suchen nach dem letzten Datensatz in der CSV-Datei nach „Zoom“ suchte, dauerte es nur eine Sekunde, um zurückzukehren und die Liste für diesen Datensatz im Handumdrehen zu erstellen.

Im AppleScript Editor verwende ich den folgenden Code, um die einfache CSV-Datei als einzelne Datei zu testen, die die 30.260 Zeilen mit 2,5 Millionen Synonymen und verwandten Wörtern enthält.

set AppleScript's text item delimiters to ""
set theMobyThesaurus to POSIX path of (path to documents folder) & "mobythes.txt"

set theSearchString to the text returned of (display dialog "Find synonyms for:" default answer "" buttons {"Cancel", "Search"} default button 2 with title "Search Moby Thesaurus")

if theSearchString is not equal to "" then

    try
        set theSearchResults to (do shell script "grep -i -m 1 '^" & theSearchString & ",' " & theMobyThesaurus)
    on error
        display dialog "No match for \"" & theSearchString & "\" available." buttons {"OK"} default button 1
        return
    end try

    if theSearchResults is not equal to "" then
        set AppleScript's text item delimiters to ","
        set theSynonymsList to items 2 thru -1 of text items of theSearchResults as list
        set AppleScript's text item delimiters to ""

        choose from list theSynonymsList with prompt "Choose a synonym for: " & linefeed & theSearchString
        if the result is not false then
            set theChosenWord to (item 1 of the result)
        end if
    end if

end if

Unter der Annahme, dass in diesem Beispiel eine Suchübereinstimmung hergestellt und nichts abgebrochen wurde, theChosenWordenthält die Variable nun das, was aus der angezeigten Liste ausgewählt wurde, und kann nach Bedarf/Wunsch weiter verarbeitet werden.

Beachten Sie, dass dies natürlich ausschließlich Beispielcode für Testzwecke ist und an Ihr Anwendungsszenario angepasst werden muss, wobei bei Bedarf eine angemessene Fehlerbehandlung integriert werden muss.

Ich glaube, dass dies der schnellste Weg sein wird, während Sie den Moby Thesaurus als einzelne CSV-Datei belassen, und wahrscheinlich schneller ist als alle Methoden, die Sie bisher ausprobiert haben.

Ich stecke in der Terminalleitung fest. Wenn ich es im Terminal eingebe, passiert nichts; Die neue .txt-Datei wird nie erstellt. > Das Terminal hängt sich einfach auf und zeigt in der nächsten Zeile ein an . Ich bin mit Terminal nicht vertraut, daher weiß ich nicht, wo das Problem liegt.
@rubiks Kugel, Entschuldigung, hatte einen Tippfehler, ließ ein 'Off des sedBefehls. Ich habe den Tippfehler korrigiert.
Meisterhaft gemacht, user3439894. Sehr nachdenkliche Antwort (z. B. Entfernen von nachgestellten Kommas aus der Quelldatei). Auch ich fand Ihre Methode erstaunlich schnell. Und Sie haben meine Gedanken über das Platzieren der Synonyme in einem choose from listDialog gelesen. (Nur für den Fall, dass Sie neugierig sind, das Skript wird dann keystrokedas vom Benutzer gewählte Synonym überschreiben und das ursprünglich ausgewählte Wort überschreiben.) Ich kann Ihnen nicht genug danken.
Ich habe festgestellt, dass der grepCode zwischen Groß- und Kleinschreibung unterscheidet. Wenn beispielsweise theSearchString, Zoomwerden keine Übereinstimmungen angezeigt. Ebenso socraticliefert eine Suche nach keine Ergebnisse. Wissen Sie, wie Sie dies beheben können, sodass die Groß- und Kleinschreibung der Zeichen theSearchStringkeine Rolle spielt?
@rubik's sphere, Geben Sie im Terminal ein man grepund drücken Sie die Eingabetaste, oder geben Sie einfach ein und grepklicken Sie mit der rechten Maustaste auf grepund wählen Sie Open man page. Ich habe die Antwort aktualisiert. BTW Wie bei vielen Befehlen können die Optionen-i -m 1 verkettet werden, dh geschrieben werden, -im 1aber nicht -m 1i.
@rubik's sphere, ich habe den set theSynonymsList ...Befehl auch gekürzt.

Ich hatte eine Lösung entwickelt, bevor user3439894 ihre Antwort gepostet hatte.

Trotz der Tatsache, dass die Lösung von user3439894 meiner Lösung in jeder Hinsicht überlegen ist, denke ich, dass ich genauso gut meinen Code posten kann, und sei es nur, um die schnelle Reaktionszeit der Lösung von user3439894 hervorzuheben .


Dateianpassungen:

Hier sind die beiden Änderungen, die ich an der Quelldatei mobythes.aur für meine Lösung vorgenommen habe:

  1. Ich habe die .aur-Datei in eine .txt-Datei konvertiert, indem ich einfach die Dateierweiterung im Finder umbenannte.

  2. Ich habe (1) Wagenrücklauf vor dem ersten Zeichen der .txt-Datei eingefügt (nur für den Fall, dass der Benutzer jemals nach dem ersten Thesaurus-Eintrag sucht, dh ) a cappella.

listMir ist aufgefallen, dass ich in meinem ursprünglichen Beitrag den falschen Baum gebellt habe – es besteht keine Notwendigkeit (oder eigentlich kein Vorteil), den Inhalt der .txt-Datei im AppleScript- Format innerhalb der Datei selbst vorzuformatieren. Daher habe ich die ursprüngliche Trennzeichenstruktur der Datei in keiner Weise geändert (so wie ich es in meinem ursprünglichen Beitrag getan hatte).


Mein Code:

display dialog "Find synonyms of:" default answer ""
set theSearchQuery to text returned of the result

-- Referencing the default delimiters of the "mobythes.txt" file:
set theOuterListDelimiter_oneCarriageReturn to (ASCII character 13)
set theInnerListDelimiter_oneComma to ","

set theSearchQueryAsAThesaurusEntry to (theOuterListDelimiter_oneCarriageReturn & theSearchQuery & theInnerListDelimiter_oneComma)

set theThesaurusAsString to (read POSIX file "/Users/Me/Desktop/mobythes.txt")

if theThesaurusAsString contains theSearchQueryAsAThesaurusEntry then

    set theSynonymsAsText to extractBetween(theThesaurusAsString, theSearchQueryAsAThesaurusEntry, theOuterListDelimiter_oneCarriageReturn)
    set theSynonymsInList to splitStringIntoList(theSynonymsAsText, theInnerListDelimiter_oneComma)

    choose from list theSynonymsInList
else
    display dialog "No thesaurus entry exists for \"" & theSearchQuery & "\"!"
end if


-- Subroutines:

to extractBetween(SearchText, startText, endText)
    --  Source: http://macscripter.net/viewtopic.php?id=24725
    set tid to AppleScript's text item delimiters -- save them for later.  
    set AppleScript's text item delimiters to startText -- find the first one.  
    set endItems to text of text item -1 of SearchText -- everything after the first.  
    set AppleScript's text item delimiters to endText -- find the end one.  
    set beginningToEnd to text of text item 1 of endItems -- get the first part.  
    set AppleScript's text item delimiters to tid -- back to original values.  
    return beginningToEnd -- pass back the piece.  
end extractBetween

on splitStringIntoList(theString, theDelimiter)
    -- Source: http://erikslab.com/2007/08/31/applescript-how-to-split-a-string/
    -- save delimiters to restore old settings:
    set oldDelimiters to AppleScript's text item delimiters
    -- set delimiters to delimiter to be used:
    set AppleScript's text item delimiters to theDelimiter
    -- create the array:
    set theArray to every text item of theString
    -- restore the old setting:
    set AppleScript's text item delimiters to oldDelimiters
    -- return the result:
    return theArray
end splitStringIntoList

Vergleich der Laufzeitleistung:

Aus Neugier habe ich zwischen dem Ansatz von user3439894 und meinem Ansatz ein "Shoot-out" der Ausführungszeiten durchgeführt.

Ich habe jeden Dialog in unseren beiden Lösungen auskommentiert. Ich habe den Testsuchbegriff als feste Zeichenfolge festgelegt, "planet".

Eingabe time osascript /Users/Me/Desktop/MyOriginalSolution.scptin Terminal.app zurückgegeben:

real    0m1.257s
user    0m0.728s
sys     0m0.409s

Eingabe time osascript /Users/Me/Desktop/user3439894Solution.scptzurückgegeben:

real    0m0.250s
user    0m0.193s
sys     0m0.030s

Basierend auf diesem Test ist die Lösung von user3439894 mehr als fünfmal schneller als meine, mit einem Unterschied von 1,007 Sekunden.