Come introdurre un oggetto lista che è memorizzato in un file .txt in un AppleScript?

3

Ho un file .scpt di AppleScript, attivato da una combinazione di tasti in FastScripts.app, che funziona come un thesaurus. Lo script cerca la parola selezionata in un elenco preformattato e, se la parola viene trovata in questo elenco, visualizza i sinonimi di questa parola all'utente 1 .

Questo elenco è contenuto in un file di testo normale (.txt). L'elenco è già formattato nel formato AppleScript list . Vorrei che il mio file .scpt fosse in grado di accettare questo testo come un vero elenco 2 .

È importante notare che il file .txt contiene 2,5 milioni di parole 3 .

Questo è il motivo per cui non sto semplicemente copiando il contenuto del file .txt nel file .scpt stesso, nonostante il fatto che il file di testo sia al 100% statico e non verrà mai modificato. Inserendo il testo direttamente nel mio script porterebbe con sé notevoli ritardi e lentezza mentre modifico e compilo il mio file .scpt in Script Editor.app.

Script Editor.app si bloccava ogni volta che provavo a leggere il file .txt. Il problema è che Script Editor legge nella memoria un determinato file di testo nella sua interezza, invece di eseguire lo streaming dei contenuti in modo più efficiente. Così ho rotto questo file di testo in 10 file di testo più piccoli 4 , ogni nuovo file .txt contenente circa 250.000 parole.

A 250.000 parole, ovviamente, i file di testo sono ancora estremamente grandi (di qualsiasi standard).

Ecco un esempio (severamente condensato) di come appare il contenuto di ciascun file di testo:

{{"exaltation","accolade","adulation","advance","advancement"},{"exalted","winnowing","winsome"},{"exam","audition","blue book","examen","examination","final","examination","test","trial","tripos","viva","written","written examination"},{"examination","Pap test","Socratic method","airing","analysis","anatomic diagnosis","appraisal","work-up","written","written examination"},{"examine","air","analyze","appraise","archetype","asleep","assess","canvass","case"},{"examiner","analyst","analyzer","asker"},{"examining","analytic","examinational","exploratory"},{"example","admonishment","admonition","alarm","archetype"},{"exasperate","bedevil","vex","work up","worry"},{"exasperated","aggravated","amplified","angry","annoyed"},{"exasperating","annoying","bothering","bothersome"}}

Come puoi vedere, il contenuto del file di testo è un elenco nidificato 5 che è organizzato nello stesso modo in cui AppleScript formatta un list . Ogni file di testo non contiene interruzioni di riga o paragrafi.

Sto cercando un metodo per ottenere questo elenco nel mio AppleScript, con il minimo possibile di latenza 6 . Questo è il motivo per cui l'ho pre-formattato. Quindi, la velocità è la chiave .

Note:

1. Lo script del mio thesaurus è simile alla funzione del thesaurus integrata che esiste in Microsoft Word. Una differenza notevole è che il mio script funziona a livello di sistema.

2. Per true list , voglio dire che posso chiamare, per esempio, item 12 di questa lista più avanti nel mio AppleScript.

3. La mia fonte per i dati del thesaurus è il Thesaurus "Moby" di Grady Ward. Ho trovato questo database da questa risposta: Alla ricerca di dati Thesaurus - Stack Overflow

4. Ho dovuto usare Hex Fiend.app per tagliare il file di testo e incollarlo in un nuovo file di testo. Non ho potuto modificare il file in TextEdit.app, senza che TextEdit si bloccasse su me.

5. L'elenco esterno contiene ogni voce del thesaurus. Gli elenchi interni contengono tutti i sinonimi per quella voce. Il primo elemento di ciascuna lista interna è il titolo della voce. Sia la lista esterna che ogni lista interna sono ordinate alfabeticamente (ad eccezione della prima parola di ogni lista interna, perché, ancora una volta, questa parola è il titolo della voce).

6. Capisco che anche il metodo più veloce avrà ancora diversi secondi di latenza, dal momento che il file di testo è così grande.

posta rubik's sphere 11.05.2017 - 12:14
fonte

2 risposte

2

Ovviamente, non conosco la portata totale di ciò che stai facendo o il modo in cui hai codificato altre cose, dato che non hai fornito tutti i dettagli e il codice, tuttavia, avrei adottato un approccio diverso.

Ho scaricato il Moby Thesaurus dalla pagina collegata nel tuo domanda e ha preformato le seguenti azioni su di esso.

  1. Estrai il contenuto del file mthes.tar.Z .
  2. Ha aperto il file ./mthes/mobythes.aur in TextWrangler e notato due cose da cambiare.
    • Cambia le terminazioni di linea da Classic Mac (CR) a Unix (LF).
    • Rimosse le virgole finali indesiderate da 6 righe.

Si noti che mentre potevo apportare queste modifiche in TextWrangler, tuttavia preferisco usare Terminal, e lo facevo usando il seguente comando:

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

Che ha richiesto, ma letteralmente, un secondo da fare (come ho effettivamente preceduto il comando precedente con time , per curiosità). Con il file mobythes.aur che è stato ora elaborato, salvato in mobythes.txt e copiato nella mia cartella Documenti, userò questo nuovo semplice file CSV così com'è, per interrogare la stringa di ricerca per una corrispondenza con il primo campo di ogni record e restituisce il record, senza il primo campo, come elenco da scegliere in AppleScript. Ho trovato questo metodo estremamente veloce, mentre cercavo lo "zoom" dell'ultimo record nel file CSV, ci volle un secondo per tornare e creare l'elenco per quel record al volo.

In AppleScript Editor utilizzo il seguente codice per testare il file CSV semplice come un singolo file contenente le 30.260 linee con 2,5 milioni di sinonimi e parole correlate.

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

In questo esempio, supponendo che sia stata effettuata una corrispondenza di ricerca e nulla è stato annullato, la variabile theChosenWord ora contiene ciò che è stato scelto dall'elenco visualizzato e può essere ulteriormente elaborato come necessario / desiderato.

Si noti che questo è chiaramente un codice di esempio a scopo di test e dovrà essere adattato allo scenario del caso d'uso, incorporando la gestione degli errori appropriata, se necessario.

Credo che questo sarà il modo più veloce lasciando il Moby Thesaurus come un singolo file CSV, ed è probabilmente più veloce di qualsiasi altro metodo tu abbia provato fino ad ora.

    
risposta data 12.05.2017 - 07:47
fonte
0

Avevo escogitato una soluzione prima che user3439894 avesse pubblicato la risposta.

Nonostante la soluzione user3439894 sia superiore alla mia soluzione in ogni modo, immagino che possa anche pubblicare il mio codice, se non altro per sottolineare il tempo di risposta veloce della soluzione user3439894 .

Rettifiche file:

Ecco le due modifiche che ho apportato al file sorgente, mobythes.aur , per la mia soluzione:

  1. Ho convertito il file .aur in un file .txt, semplicemente rinominando l'estensione del file in Finder.

  2. Ho inserito (1) il ritorno a capo prima del primo carattere del file .txt (nel caso in cui l'utente cerchi sempre la prima voce del thesaurus, cioè a cappella ).

Mi sono reso conto che stavo abbaiando dall'albero sbagliato nel mio post originale - non c'è bisogno (o beneficio, in realtà) di pre-formattare il contenuto del file .txt nel formato list di AppleScript, all'interno del file stesso. Quindi, non ho modificato la struttura del delimitatore originale del file (nel modo in cui l'avevo fatto nel mio post originale).

Il mio codice:

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

Confronto delle prestazioni in runtime:

Per pura curiosità, ho condotto uno "shoot-out" dei tempi di esecuzione tra l'approccio user3439894 e il mio approccio.

Ho commentato ogni dialogo in entrambe le nostre soluzioni. Ho impostato il termine di ricerca del test come stringa fissa, "planet" .

Inserimento time osascript /Users/Me/Desktop/MyOriginalSolution.scpt in Terminal.app restituito:

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

Inserimento time osascript /Users/Me/Desktop/user3439894Solution.scpt restituito:

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

In base a questo test, la soluzione user3439894 è oltre 5 volte più veloce della mia, con una differenza di 1.007 secondi.

    
risposta data 13.05.2017 - 08:58
fonte

Leggi altre domande sui tag