¿Cómo introducir un objeto de lista que se almacena en un archivo .txt en un AppleScript?

3

Tengo un archivo .scpt de AppleScript, activado por una combinación de teclas en FastScripts.app, que funciona como un tesauro. 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 plano (.txt). La lista ya está formateada en el formato AppleScript list . Me gustaría que mi archivo .scpt pueda aceptar este texto como una lista verdadera 2 .

Es importante tener en cuenta que el archivo .txt contiene 2.5 millones de palabras 3 .

Es por esto que no estoy simplemente copiando el contenido del archivo .txt en el propio archivo .scpt, a pesar de que el archivo de texto es 100% estático y nunca se modificará. Insertar el texto directamente en mi script traería consigo un considerable retraso y lentitud a medida que edito y compilo mi archivo .scpt en Script Editor.app.

Script Editor.app se congeló cada vez que intentaba leer el archivo .txt. El problema es que el Editor de secuencias de comandos lee en la memoria un archivo de texto determinado 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 , y cada nuevo archivo .txt contiene aproximadamente 250,000 palabras.

Por supuesto, con 250,000 palabras, los archivos de texto aún son extremadamente grandes (según cualquier estándar).

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

  

{{"exaltación", "elogio", "adulación", "avance", "avance"}, {"exaltado", "avivando", "winsome"}, {"examen", "audición", " libro azul "," examen "," examen "," final "," examen "," prueba "," juicio "," tripos "," viva "," escrito "," examen escrito "}, {" examen " , "Prueba de Papanicolaou", "Método socrático", "Transferencia", "Análisis", "Diagnóstico anatómico", "Evaluación", "Elaboración", "Escrito", "Examen escrito"}, {"Examinar", " aire "," analizar "," evaluar "," arquetipo "," dormido "," evaluar "," examinar "," caso "}, {" examinador "," analista "," analizador "," interrogador "}, {"Examinando", "Analítico", "Examinador", "Exploratorio"}, {"Ejemplo", "Amonestación", "Advertencia", "Alarma", "Arquetipo"}, {"Exasperate", "Bedevil", " vex "," trabajar "," preocuparse "}, {" exasperado "," agravado "," amplificado "," enojado "," molesto "}, {" exasperante "," molesto "," molestar "," 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 a 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 . Por eso lo preformateé. Entonces, la velocidad es clave .

Footnotes:

1. El script de mi tesauro es similar a la característica de tesauro incorporada que existe en Microsoft Word. Una diferencia notable es que mi script funciona en todo el sistema.

2. Por true list , quiero decir que puedo llamar, por ejemplo, item 12 de 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 en esta respuesta: Buscando datos del tesauro - Desbordamiento de pila

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 congele.

5. La lista externa 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 externa como cada lista interna están ordenadas alfabéticamente (con la excepción de la primera palabra de cada lista interna porque, nuevamente, esta palabra es el título de la entrada).

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

pregunta rubik's sphere 11.05.2017 - 12:14

2 respuestas

2

Obviamente, no conozco el alcance total de lo que estás haciendo o cómo tienes otras cosas codificadas, ya que no has proporcionado todos los detalles y el código, sin embargo, adoptaría un enfoque diferente.

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

  1. Se extrajeron los contenidos del archivo mthes.tar.Z .
  2. Abrió el archivo ./mthes/mobythes.aur en TextWrangler y notó dos cosas que 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 podría hacer estos cambios en TextWrangler, sin embargo, 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 para hacer (ya que realicé el comando anterior con time , por curiosidad). Con el archivo mobythes.aur ahora procesado, guardado en mobythes.txt y copiado en mi carpeta de Documentos, usaré este nuevo archivo CSV plano tal como está, para consultar la cadena de búsqueda para una coincidencia con el primer campo de cada registro y devuelva el registro, sin el primer campo, como una lista para elegir en AppleScript. Descubrí que este método es extremadamente rápido, mientras buscaba "zoom" en el último registro en el archivo CSV, solo tardó un segundo en volver y crear la lista para ese registro sobre la marcha.

En AppleScript Editor, utilizo el siguiente código para probar el archivo CSV sin formato 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, asumiendo que se hizo una coincidencia de búsqueda y no se canceló nada, entonces la variable theChosenWord ahora contiene lo que se eligió de la lista mostrada y puede procesarse más según sea necesario / deseado.

Tenga en cuenta que este es, por supuesto, un código estrictamente de ejemplo para fines de prueba y deberá adaptarse al escenario de su 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 de dejar el Moby Thesaurus como un solo archivo CSV, y es probablemente más rápido que cualquier método que hayas probado hasta ahora.

    
respondido por el user3439894 12.05.2017 - 07:47
0

Había ideado una solución antes de que user3439894 publicara su respuesta.

A pesar del hecho de que la solución de user3439894 es superior a mi solución en todos los sentidos, me imagino 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 en el archivo fuente, mobythes.aur , para mi solución:

  1. He convertido el archivo .aur en un archivo .txt, simplemente cambiando el nombre de la extensión de archivo en el Finder.

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

Me di cuenta de que estaba ladrando el árbol incorrecto en mi publicación original: no hay ninguna necesidad (o beneficio, en realidad) de formatear previamente el contenido del archivo .txt en el formato list de AppleScript, dentro del propio archivo. Por lo tanto, no modifiqué la estructura delimitadora original del archivo (como 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 del rendimiento en tiempo de ejecución:

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

Comenté todos los diálogos en nuestras dos soluciones. Establecí el término de búsqueda de prueba como la cadena fija, "planet" .

Devolvió el ingreso de time osascript /Users/Me/Desktop/MyOriginalSolution.scpt en Terminal.app:

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

Ingresando time osascript /Users/Me/Desktop/user3439894Solution.scpt devuelto:

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.

    
respondido por el rubik's sphere 13.05.2017 - 08:58

Lea otras preguntas en las etiquetas