¿Cómo introducir un objeto de lista que está almacenado en un archivo .txt en un AppleScript?

Tengo un archivo .scpt de AppleScript, activado por una combinación de teclas en FastScripts.app, que funciona como un diccionario de sinónimos. El script busca la palabra seleccionada en una lista preformateada y, si la palabra se encuentra en esta lista, muestra los sinónimos de esta palabra al usuario 1 .

Esta lista está contenida en un archivo de texto sin formato (.txt). La lista ya está formateada en listformato AppleScript. Me gustaría que mi archivo .scpt pueda aceptar este texto como una lista verdadera 2 .

Es importante señalar que el archivo .txt contiene 2,5 millones de palabras 3 .

Es por eso que no copio simplemente el contenido del archivo .txt en el archivo .scpt, a pesar de que el archivo de texto es 100 % estático y nunca se modificará. Insertar el texto directamente en mi secuencia de comandos traería consigo un retraso y una lentitud considerables mientras edito y compilo mi archivo .scpt en Script Editor.app.

Script Editor.app se congelaba cada vez que intentaba leer el archivo .txt. El problema es que Script Editor lee en la memoria un archivo de texto dado en su totalidad, en lugar de transmitir los contenidos de una manera más eficiente. Entonces, dividí este archivo de texto en 10 archivos de texto más pequeños 4 , cada nuevo archivo .txt contiene alrededor de 250 000 palabras.

Por supuesto, con 250.000 palabras, los archivos de texto siguen siendo extremadamente grandes (desde cualquier punto de vista).

Aquí hay un ejemplo (muy resumido) de cómo se ve el contenido de cada archivo de texto:

{{"exaltación","galardón","adulación","avanzar","adelanto"},{"exaltado","aventar","ganador"},{"examen","audición","libro azul" ,"examen","examen","final","examen","test","ensayo","tripos","viva","escrito","examen escrito"},{"examen","Pap prueba","Método socrático","ventilación","análisis","diagnóstico anatómico","evaluación","elaboración","escrito","examen escrito"},{"examinar","aire", "analizar","valorar","arquetipo","dormido","evaluar","lienzo","caso"},{"examinador","analista","analizador","preguntador"},{"examinando","analítico","examinatorio","exploratorio"},{"ejemplo","advertencia"," amonestación","alarma","arquetipo"},{"exasperado","molesto","vex","exagerado","preocupación"},{"exasperado","agravado","amplificado","enojado ","molesto"},{"exasperante","molesto","molesto","molesto"}}alarma","arquetipo"},{"exasperado","atormentado","vex","exasperado","preocupado"},{"exasperado","agravado","amplificado","enojado","molesto "},{"exasperante","molesto","molesto","molesto"}}alarma","arquetipo"},{"exasperado","atormentado","vex","exasperado","preocupado"},{"exasperado","agravado","amplificado","enojado","molesto "},{"exasperante","molesto","molesto","molesto"}}

Como puede ver, el contenido del archivo de texto es una lista anidada 5 que está organizada de la misma manera que AppleScript formatea un archivo list. Cada archivo de texto no contiene saltos de línea ni párrafos.

Estoy buscando un método para incluir esta lista en mi AppleScript, con la menor latencia posible 6 . Es por eso que lo formateé previamente. Por lo tanto, la velocidad es clave .


Notas al pie:

1. La secuencia de comandos de mi diccionario de sinónimos es similar a la función de diccionario de sinónimos integrada que existe en Microsoft Word. Una diferencia notable es que mi script funciona en todo el sistema.

2. Por lista verdadera , quiero decir que puedo llamar, por ejemplo, item 12a esta lista más adelante en mi AppleScript.

3. Mi fuente para los datos del tesauro es el Tesauro "Moby" de Grady Ward. Encontré esta base de datos de esta respuesta: Buscando datos de tesauro programandonet.com

4. Tuve que usar Hex Fiend.app para cortar el archivo de texto y pegarlo en un nuevo archivo de texto. No pude editar el archivo en TextEdit.app, sin que TextEdit se congelara.

5. La lista exterior contiene cada entrada del tesauro. Las listas internas contienen todos los sinónimos de esa entrada. El primer elemento de cada lista interna es el título de la entrada. Tanto la lista exterior como cada lista interior están ordenadas alfabéticamente (con la excepción de la primera palabra de cada lista interior, porque, de nuevo, esta palabra es el título de la entrada).

6. Entiendo que incluso el método más rápido tendrá varios segundos de latencia, ya que el archivo de texto es muy grande.


¿Por qué no usas una base de datos SQLite?
Nunca había oído hablar de SQLite, hasta que lo mencionaste. Lo desconozco por completo. Sin embargo, si sabe cómo lograr lo que quiero con él, estoy feliz de usar cualquier cosa.
¿Cuál es el propósito de los 2.5 MWwords? La idea detrás de mi propuesta es utilizar una base de datos en lugar de un archivo de texto básicamente plano.
Por favor, vea mi edición más reciente. He proporcionado más detalles sobre mi script.

Respuestas (2)

Obviamente, no sé el alcance total de lo que está haciendo o cómo tiene otras cosas codificadas, ya que no proporcionó todos los detalles y el código; sin embargo, tomaría un enfoque diferente.

Descargué el Moby Thesaurus de la página vinculada en su pregunta y realicé las siguientes acciones en él.

  1. Extrajo el contenido del mthes.tar.Zarchivo.
  2. Abrió el ./mthes/mobythes.aurarchivo en TextWrangler y notó dos cosas para cambiar.
    • Cambie los finales de línea de Classic Mac (CR) a Unix (LF).
    • Se eliminaron las comas finales no deseadas de 6 líneas.

Tenga en cuenta que si bien pude hacer estos cambios en TextWrangler, prefiero usar Terminal y lo hice con el siguiente comando:

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

Lo que tomó literalmente un segundo (ya que en realidad prediqué el comando anterior con time, por curiosidad). Ahora que el mobythes.aurarchivo ha sido procesado, guardado mobythes.txty copiado en mi carpeta Documentos, usaré este nuevo archivo CSV sin formato tal como está, para consultar la cadena de búsqueda en busca de una coincidencia con el primer campo de cada registro y devolver el registro, sin el primer campo, como una lista para elegir en AppleScript. Encontré que este método es extremadamente rápido, mientras buscaba "acercar" el último registro en el archivo CSV, solo tomó un segundo regresar y crear la lista para ese registro sobre la marcha.

En AppleScript Editor utilizo el siguiente código para compararlo con el archivo CSV simple como un solo archivo que contiene las 30 260 líneas con 2,5 millones de sinónimos y palabras relacionadas.

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

En este ejemplo, suponiendo que se hizo una coincidencia de búsqueda y no se canceló nada, la theChosenWordvariable ahora contiene lo que se eligió de la lista mostrada y se puede procesar más según sea necesario o deseado.

Tenga en cuenta que, por supuesto, este es un código estrictamente de ejemplo para fines de prueba y deberá adaptarse a su escenario de caso de uso al tiempo que incorpora el manejo de errores adecuado según sea necesario.

Creo que esta será la forma más rápida mientras deja el Tesauro de Moby como un solo archivo CSV, y probablemente sea más rápido que cualquier método que haya probado hasta ahora.

Estoy atascado en la línea de la Terminal. Cuando lo ingreso en Terminal, no ocurre nada; el nuevo archivo .txt nunca se crea. La terminal simplemente se cuelga y muestra una > en la siguiente línea. No estoy familiarizado con Terminal, así que no sé cuál es el problema.
La esfera de @rubik, lo siento, tuvo un error tipográfico, dejó un comando 'fuera de lugar sed. Arreglé el error tipográfico.
Hecho magistralmente, usuario3439894. Respuesta muy reflexiva (por ejemplo, eliminar las comas finales del archivo fuente). Yo también encontré que su método es increíblemente rápido. Y me leíste la mente acerca de colocar los sinónimos dentro de un choose from listdiálogo. (En caso de que sienta curiosidad, el script mostrará keystrokeel sinónimo que elija el usuario, sobrescribiendo la palabra original que se seleccionó). No puedo agradecerles lo suficiente.
Me he dado cuenta de que el grepcódigo distingue entre mayúsculas y minúsculas. Por ejemplo, si theSearchStringes Zoom, no aparecerán coincidencias. Del mismo modo, una búsqueda de socraticno produce resultados. ¿Sabes cómo arreglar esto, para que theSearchStringno importe el caso de los personajes?
Esfera de @rubik, en Terminal, escriba man grepy presione Intro, o simplemente escriba grepy haga clic con el botón derecho en grepy seleccione Open man page. He actualizado la respuesta. Por cierto, como con muchos comandos, las opciones se pueden concatenar, es decir, se -i -m 1pueden escribir -im 1pero no -m 1i.
Esfera de @rubik, también acorté el set theSynonymsList ...comando.

Había ideado una solución antes de que el usuario 3439894 publicara su respuesta.

A pesar de que la solución de user3439894 es superior a mi solución en todos los sentidos, creo que también puedo publicar mi código, aunque solo sea para resaltar el rápido tiempo de respuesta de la solución de user3439894 .


Ajustes de archivo:

Aquí están las dos modificaciones que hice al archivo fuente, mobythes.aur , para mi solución:

  1. Convertí el archivo .aur en un archivo .txt, simplemente cambiando el nombre de la extensión del archivo en Finder.

  2. Inserté (1) retorno de carro antes del primer carácter del archivo .txt (en caso de que el usuario alguna vez busque la primera entrada del diccionario de sinónimos, es decir, a cappella).

Me di cuenta de que estaba ladrando en el árbol equivocado en mi publicación original: no hay necesidad (ni beneficio, en realidad) de formatear previamente el contenido del archivo .txt en el formato de AppleScript, listdentro del archivo mismo. Por lo tanto, no modifiqué la estructura delimitadora original del archivo en absoluto (de la forma en que lo había hecho en mi publicación original).


Mi código:

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

Comparación de rendimiento en tiempo de ejecución:

Por curiosidad, realicé un "tiroteo" de los tiempos de ejecución entre el enfoque de user3439894 y mi enfoque.

Comenté cada diálogo en nuestras dos soluciones. Configuré el término de búsqueda de prueba como la cadena fija, "planet".

Ingresando time osascript /Users/Me/Desktop/MyOriginalSolution.scpten Terminal.app devolvió:

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

Entrando time osascript /Users/Me/Desktop/user3439894Solution.scptdevuelto:

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

Según esta prueba, la solución de user3439894 es 5 veces más rápida que la mía, con una diferencia de 1,007 segundos.