Documentos de Académico
Documentos de Profesional
Documentos de Cultura
• Un interactivo
Corrector ortográfico
• Generando un
Índice formateado
• Detalles de repuesto de la
índice maestro
Prog ram
Este capítulo presenta dos aplicaciones complejas que integran la mayoría de las
características del lenguaje de programación awk. El primer programa, el corrector
ortográfico, proporciona una interfaz interactiva para el programa de ortografía de UNIX.
La segunda aplicación, índice maestro, es un programa por lotes para generar un índice
para un libro o un conjunto de libros. Incluso si no está interesado en la aplicación en
particular, debe estudiar estos programas más grandes para tener una idea del alcance de
los problemas que un programa awk puede resolver.
El programa de corrección ortográfica ofrece otra forma: le muestra cada palabra que el
hechizo ha encontrado y le pregunta si desea corregir la palabra. Puede cambiar cada
aparición de la palabra después de ver la línea en la que aparece, o puede corregir el error
ortográfico a nivel mundial. También puede optar por agregar cualquier palabra que se
deletrea en un archivo de diccionario local.
Usando la lista de palabras “mal escritas” aparecidas por hechizo, el corrector ortográfico
le indica al usuario que las corrija. Antes de que se muestre la primera palabra, se muestra
una lista de respuestas que describe qué acciones son posibles.
Respuestas:
Cambiar cada ocurrencia,
Cambio global,
Añadir a Dict,
Ayuda,
Dejar
CR para ignorar:
1 - SparcStation encontrado (C / G / A / H / Q /): a
El usuario ingresa "c" para cambiar cada ocurrencia. Esta respuesta permite al usuario ver
la línea que contiene la falta de ortografía y luego hacer el cambio. Después de que el
usuario haya realizado cada cambio, se muestran las líneas modificadas y se le pide al
usuario que confirme si guarda los cambios.
No está claro si la quinta palabra es una falta de ortografía o no, por lo que el usuario
ingresa "c" para ver la línea.
5 - Encontrado xvf (C / G / A / H / Q /): c
nombre de archivo tar xvf
ˆˆˆ
Cambiar a: VOLVER
Después de que se hayan procesado todas las palabras de la lista, o si el usuario se cierra
antes, se le solicitará que guarde los cambios realizados en el documento y el diccionario.
Comenzar procedimiento
El procedimiento BEGIN para spellcheck.awk es grande. También es algo inusual.
# spellcheck.awk - corrector ortográfico interactivo
#
# AUTOR: Dale Dougherty
##
Un corrector ortográfico interactivo 281
EMPEZAR {
# Procesar argumentos de línea de comandos
# Debe tener al menos dos argumentos: nawk y nombre de archivo si (ARGC> 1) {
# si hay más de dos argumentos, el segundo argumento es dict if (ARGC> 2) {
# prueba para ver si dict se especifica con "+"
# y asignar ARGV [1] a SPELLDICT
si (ARGV [1] ˜ /ˆ\+.*/)
SPELLDICT = ARGV [1]
más
SPELLDICT = "+" ARGV [1]
# asignar archivo ARGV [2] a SPELLFILE
SPELLFILE = ARGV [2]
# eliminar args para que awk no los abra como archivos eliminar ARGV [1]
eliminar ARGV [2]
}
# no más de dos args más {
# asignar archivo ARGV [1] a SPELLFILE
SPELLFILE = ARGV [1]
# prueba para ver si el archivo dict local existe si (! system ("test -r dict")) {
# si es así, pregunte si deberíamos usarlo
printf ("¿Usar archivo dict local? (s / n)")
getline responder <"-"
# si la respuesta es sí, use "dict"
if (respuesta ˜ / [aa] (es)? /) {
SPELLDICT = "+ dict"
}
}
}
} # fin del procesamiento de argumentos> 1
# si args no es> 1, imprima el uso del comando shell de otra manera {
}
# fin de procesamiento de argumentos de línea de comando
282 Capítulo 12: Aplicaciones con todas las
funciones
# crear nombres de archivo temporales, cada uno comienza con sp_ wordlist = "sp_wordlist"
spellsource = "sp_input"
spellout = "sp_out"
# copiar SPELLFILE al sistema de archivos de entrada temporal (fuente de hechizos "cp" SPELLFILE "")
# prueba la lista de palabras para ver si aparecen las palabras mal escritas
if (system (lista de palabras "test -s")) {
# si la lista de palabras está vacía (o el comando de deletreo falló), salga de print "No se
encontraron palabras mal escritas". sistema ("rm" spellsource "" lista de palabras)
salida
}
# asigne un archivo de lista de palabras a ARGV [1] para que awk lo lea. ARGV [1] = lista de palabras
Una vez que hemos procesado los argumentos, los eliminamos de la matriz ARGV. Esto
es para evitar que se interpreten como argumentos de nombre de archivo.
Un corrector ortográfico interactivo 283
La tercera parte del procedimiento ejecuta un hechizo y crea una lista de palabras.
Probamos para ver que este archivo existe y que hay algo en él antes de continuar. Si por
alguna razón el programa de ortografía falla, o no se encuentran palabras mal escritas, el
archivo de la lista de palabras estará vacío. Si este archivo existe, asignamos el nombre
del archivo como el segundo elemento en la matriz ARGV. Esta es una forma inusual
pero válida de proporcionar el nombre del archivo de entrada que awk procesará. ¡Tenga
en cuenta que este archivo no existía cuando se invocó awk! El nombre del archivo del
documento, que se especificó en la línea de comando, ya no está en la matriz ARGV. No
leeremos el archivo del documento utilizando el bucle de entrada principal de awk. En
cambio, un ciclo while lee el archivo para encontrar y corregir palabras mal escritas.
Procedimiento principal
El procedimiento principal es bastante pequeño, simplemente muestra una palabra mal
escrita y le pide al usuario que ingrese una respuesta apropiada. Este procedimiento se
ejecuta para cada palabra mal escrita.
Una razón por la cual este procedimiento es breve es porque la acción central —
corrección de una palabra mal escrita— es manejada por dos funciones más grandes
definidas por el usuario, que veremos en la última sección.
{
# asignar palabra a la falta de ortografía incorrecta = $ 1 respuesta = 1 ++ palabra
284 Capítulo 12: Aplicaciones con todas las
funciones
# si se hizo un cambio
if (cambios) {
# mostrar líneas cambiadas
para (j = 1; j <= cambios; ++ j)
imprimir cambiadoLíneas [j]
printf ("% d líneas cambiadas", cambios)
# función para confirmar antes de guardar los cambios confirm_changes ()
}
}
# Cambiar globalmente
if (respuesta ˜ / [gG] (lobal)? /) {
# llamar a la función para solicitar la corrección
# y muestra cada línea que se cambia.
# Solicite al usuario que apruebe todos los cambios antes de guardar. make_global_change ()
}
} # fin del procedimiento principal
Un corrector ortográfico interactivo 285
El primer campo de cada línea de entrada de la lista de palabras contiene la palabra mal
escrita y se asigna a la falta de ortografía. Construimos un ciclo while dentro del cual
mostramos la palabra mal escrita al usuario y solicitamos una respuesta. Mire
detenidamente la expresión regular que prueba el valor de la respuesta:
while (respuesta! ˜ / (ˆ [cCgGaAhHqQ]) | ˆ $ /)
El usuario solo puede salir de este ciclo ingresando cualquiera de las letras especificadas
o ingresando un retorno de carro n, una línea vacía. El uso de expresiones regulares para
probar la entrada del usuario ayuda enormemente a escribir un programa simple pero
flexible. El usuario puede ingresar una sola letra “c” en minúscula o mayúscula o una
palabra que comience con “c” como “Cambiar”.
La siguiente respuesta es "dejar de fumar". La acción asociada con salir es salir, que
abandona el procedimiento principal y va al procedimiento FIN.
Las respuestas "Cambio" y "Global" hacen que comience el verdadero trabajo del
programa. Es importante entender cómo difieren. Cuando el usuario ingresa “c” o
“cambio”, se muestra la primera aparición de la palabra mal escrita en el documento.
Luego se le solicita al usuario que realice el cambio. Esto sucede para cada aparición en
el documento. Cuando el usuario ingresa "g" o "global", se le solicita que realice el
cambio de inmediato, y todos los cambios se realizan a la vez sin que el usuario confirme
cada uno. Este trabajo es manejado en gran parte por dos funciones, make_change () y
make_global_change (), que veremos en la última sección. Estas son todas las respuestas
válidas, excepto una. Un carro devuelto significa ignorar la palabra mal escrita y obtener
la siguiente palabra en la lista. Esta es la acción predeterminada del bucle de entrada
principal,
Procedimiento FINAL
El procedimiento END, por supuesto, se alcanza en una de las siguientes circunstancias:
• El comando de hechizo falló o no apareció ningún error ortográfico.
• La lista de palabras mal escritas está agotada.
• El usuario ha ingresado "salir" en un mensaje.
El propósito del procedimiento END es permitir al usuario confirmar cualquier cambio
permanente en el documento o el diccionario.
286 Capítulo 12: Aplicaciones con todas las
funciones
FIN {
# si llegamos aquí después de leer solo un registro,
# no se hicieron cambios, así que salga.
si (NR <= 1) sale
# el usuario debe confirmar guardar las correcciones en el archivo
while (saveAnswer! ˜ / ([aa] (es)?) | ([nN] o?) /) {
printf "¿Guardar correcciones en% s (y / n)?", SPELLFILE
getline saveAnswer <"-"
}
# si la respuesta es sí, entonces el archivo de entrada temporal de mv a SPELLFILE
# guardar viejo SPELLFILE, por si acaso
if (saveAnswer ˜ / ˆ [aa] /) {
sistema ("cp" SPELLFILE "" SPELLFILE ".orig")
sistema ("mv" fuente de hechizos "" SPELLFILE)
}
# si la respuesta es no, entonces rm archivo de entrada temporal if (saveAnswer ˜ / ˆ [nN] /)
sistema (fuente de hechizos "rm")
El procedimiento FIN comienza con una declaración condicional que prueba que el
número de registros es menor o igual a 1. Esto ocurre cuando el programa de ortografía
no genera una lista de palabras o cuando el usuario ingresa "salir" después de ver solo el
primer grabar. Si es así, se cierra el procedimiento END ya que no hay trabajo que
guardar.
Un corrector ortográfico interactivo 287
A continuación, creamos un ciclo while para preguntarle al usuario sobre cómo guardar
los cambios realizados en el documento. Requiere que el usuario responda "y" o "n" a la
solicitud. Si la respuesta es "y", el archivo de entrada temporal reemplaza el archivo de
documento original. Si la respuesta es "n", se elimina el archivo temporal. No se aceptan
otras respuestas.
A continuación, probamos para ver si la matriz dict tiene algo en ella. Sus elementos son
las palabras que se agregarán al diccionario. Si el usuario aprueba agregarlos al
diccionario, estas palabras se agregan al diccionario actual, como se definió
anteriormente, o si no, a un archivo dict local. Debido a que el diccionario debe ordenarse
para que se lea por hechizo, se ejecuta un comando sor t con la salida enviada a un
archivo temporal que luego se copia sobre el archivo original.
Funciones de soporte
Existen tres funciones de soporte, dos de las cuales son grandes y hacen la mayor parte
del trabajo de hacer cambios en el documento. La tercera función respalda ese trabajo al
confirmar que el usuario desea guardar los cambios realizados.
# ensamblarlo
línea = substr ($ 0,1, len-1) $ 0 =
línea stringToChange
}
más {
$ 0 = stringToChange si
(madechg) ++ cambia
}
# crear una subcadena para que podamos intentar hacer coincidir otras ocurrencias len + = RSTART +
RLENGTH
part1 = substr ($ 0, 1, len-1) part2 =
substr ($ 0, len)
# se llama a sí mismo para ver si se encuentra la falta de ortografía en la parte restante make_change
(part2, len)
} # fin de if
} # final de make_change ()
si debe ser corregido Debajo de la pantalla de la línea actual hay una fila de carros que
indica la palabra mal escrita.
Otras dos utilidades que se encuentran en el sistema UNIX
ˆˆˆˆˆˆˆˆˆˆ
Después de mostrar la línea, la función solicita al usuario que ingrese una corrección.
Luego sigue mostrando lo que el usuario ha ingresado y solicita confirmación. Si se
aprueba la corrección, se llama a la función sub () para realizar el cambio. Si no se
aprueba, el usuario tiene otra oportunidad de ingresar la palabra correcta.
Recuerde que la función sub () solo cambia la primera aparición en una línea. La función
gsub () cambia todas las ocurrencias en una línea, pero queremos permitir que el usuario
confirme cada cambio. Por lo tanto, debemos tratar de hacer coincidir la palabra mal
escrita con la parte restante de la línea. Y tenemos que poder hacer coincidir la siguiente
aparición, independientemente de si la primera ocurrencia se modificó o no.
Para hacer esto, make_change () está diseñado como una función recursiva; se llama a sí
mismo para buscar ocurrencias adicionales en la misma línea. En otras palabras, la
primera vez que se llama a make_change (), mira todos los $ 0 y coincide con la primera
palabra mal escrita en esa línea. Luego divide la línea en dos partes: la primera parte
contiene los caracteres hasta el final de la primera aparición y la segunda parte contiene
los caracteres que siguen inmediatamente al final de la línea. Luego se llama a sí mismo
para tratar de hacer coincidir la palabra mal escrita en la segunda parte. Cuando se llama
recursivamente, la función toma dos argumentos.
La variable madechg tendrá un valor si la función sub () fue exitosa. $ 0 (las dos partes se
han unido) se asignan a un elemento de la matriz. Cuando se han leído todas las líneas del
documento, el procedimiento principal realiza un bucle a través de esta matriz para
mostrar todas las líneas modificadas. Luego llama a la función confir m_changes () para
preguntar si estos cambios deben guardarse. Copia el archivo de salida temporal sobre el
archivo de entrada temporal, manteniendo intactas las correcciones realizadas para la
palabra mal escrita actual.
}
# escribir todas las líneas en el archivo de salida temporal imprimir>
deletrear
} # final del ciclo while para leer el archivo
make_global_change ()
Esta función solicita al usuario que ingrese una corrección. Se configura un bucle while
para leer todas las líneas del documento y aplicar la función gsub () para realizar los
cambios. La diferencia principal es que todos los cambios se realizan a la vez: no se le
pide al usuario que los confirme. Cuando se han leído todas las líneas, la función muestra
las líneas que se modificaron y llama a m_changes () para confirmar que el usuario
aplique este lote de cambios antes de guardarlos.
Este script configura una variable de shell AWKLIB que especifica la ubicación del
script spellcheck.awk. El símbolo "$ *" se expande a todos los parámetros de la línea de
comandos siguiendo el nombre del script. Estos parámetros están disponibles para awk.
Una de las cosas interesantes sobre este corrector ortográfico es lo poco que se hace en el
**
script de shell. Todo el trabajo se realiza en el lenguaje de programación awk, incluida
la ejecución de 10 comandos UNIX. Estamos utilizando una sintaxis consistente y las
mismas estructuras al hacerlo todo en awk. Cuando tiene que hacer parte de su trabajo en
el shell y algunos en awk, puede ser confuso. Por ejemplo, debe recordar las diferencias
en la sintaxis de if condicionales y cómo hacer referencia a las variables. Las versiones
modernas de awk proporcionan una verdadera alternativa al shell para ejecutar comandos
e interactuar con un usuario. La lista completa de spellcheck.awk se encuentra en el
Apéndice C, Suplemento para el Capítulo 12.
Este proceso sigue siendo prácticamente el mismo si se utiliza trof f, otro lote codificado
para asuntos o un formateador WYSIWYG como FrameMaker, aunque los pasos no
están tan claramente separados con este último. Sin embargo, describiré cómo usamos
trof f para generar un índice como el de este libro. Codificamos el índice usando las
siguientes macros:
Macro Descripción
.XX Produce entradas de índice general.
.XN Crea referencias cruzadas "ver" o "ver también".
.XB Crea una entrada de página en negrita que indica la referencia principal.
.XS Comienza el rango de páginas para la entrada.
.XE Finaliza el rango de páginas para la entrada.
Estas macros toman un solo argumento entre comillas, que puede tener uno de varios
para ms, indicando claves primarias, secundarias o terciarias:
"primario [: secundario [; terciario ]] "
* Procesamiento de texto UNIX (Dougherty y O'Reilly, Howard W. Sams, 1987) presenta un corrector ortográfico
basado en sed que depende en gran medida del caparazón. Es interesante comparar las dos versiones.
Generando un índice enmarañado 293
Se utilizan dos puntos como separador entre las claves primaria y secundaria. Para
soportar una convención de codificación anterior, la primera coma se interpreta como el
separador si no se utilizan dos puntos. Un punto y coma indica la presencia de una clave
terciaria. El número de página siempre está asociado con la última clave.
Su e es una entrada con solo una clave primaria:
.XX "XView"
La entrada "ver" refiere a una persona a otra entrada de índice. El "ver también" se usa
normalmente cuando hay entradas para, en este caso, "correo electrónico mh", pero hay
información relevante catalogada con otro nombre. Solo las entradas "ver" no tienen
números de página asociados a ellas.
XView 42
XView: nombres reservados43
XView, paquetes 43
XView: objetos; lista de 43
XView: objetos; jerarquía de 44
XView, paquetes 45
recuperación de errores: (Ver manejo de errores)
mh mailer: (ver también xmh mailer) 46
Estas entradas sirven como entrada para el programa de indexación. Cada entrada
(excepto las entradas "ver") consta de la clave y un número de página. En otras palabras,
la entrada se divide en dos partes y la primera parte, la clave, también se puede dividir en
tres partes. Cuando estas entradas son procesadas por el programa de indexación y la
salida es para matizada, las entradas para "XView" se combinan de la siguiente manera:
XView, 42
objetos; jerarquía de, 44;
lista de, 43
paquetes, 43,45
nombres reservados, 43
294 Capítulo 12: Aplicaciones con todas las
funciones
Esto es lo que hace el programa de índice si está procesando las entradas de índice para
un solo libro. También le permite crear un índice maestro, un índice general para un
conjunto de volúmenes. Para hacer eso, una secuencia de comandos awk agrega un
número romano o una abreviatura después del número de página. Cada archivo contiene
las entradas para un libro en particular y esas entradas se identifican de forma única. Si
optamos por usar números romanos para identificar el volumen, las entradas anteriores se
cambiarían a:
XView 42: yo
XView: nombres reservados43: I
XView: objetos; lista de 43: I
Con entradas de varios volúmenes, el índice final que se genera podría verse así:
XView, I: 42; II: 55,69,75
objetos; jerarquía de, I: 44;
lista de, I: 43; II: 56
paquetes, I: 43,45
nombres reservados, I: 43
Por ahora, solo es importante reconocer que la entrada de índice utilizada como entrada
para el programa awk puede tener un número de página o un número de página seguido
de un identificador de volumen.
* Los orígenes de este programa de indexación se remontan a una copia de un programa de indexación escrito en
awk por Steve Talbott. Aprendí este programa al desarmarlo e hice algunos cambios para admitir la numeración de
páginas consecutivas además de la numeración de páginas de sección. Ese fue el programa que describí en UNIX
Text Processing. Conociendo ese programa, escribí un programa de indexación que podría tratar con entradas de
índice producidas por Microsoft Word y generar un índice usando la numeración de páginas de sección. Más tarde,
necesitábamos un índice maestro para varios libros de nuestra serie X Window System. Aproveché la oportunidad
para repensar nuestro programa de indexación y reescribirlo usando nawk, para que sea compatible con los índices de
un solo libro y de varios libros. El lenguaje de programación AWK contiene un ejemplo de un programa de índice
que es más pequeño que el que se muestra aquí y podría ser un lugar para comenzar si lo encuentra demasiado
complicado. Sin embargo, no trata con llaves. Ese programa de indexación es una versión simplificada del descrito en
el Informe técnico 128 de Bell Labs Computing Science, Herramientas para imprimir índices, octubre de 1986, por
Brian Kernighan y Jon Bentley. [DD]
Generando un índice enmarañado 295
Después de las descripciones de cada uno de los módulos del programa, una sección final
discute algunos detalles restantes. En su mayor parte, estos son fragmentos de código que
se ocupan de problemas esenciales relacionados con las entradas que tuvieron que
* *
resolverse en el camino. El índice maestro del script de shell permite al usuario
especificar varias opciones de línea de comandos diferentes para especificar qué tipo de
índice hacer e invoca los programas awk necesarios para hacer el trabajo. Las
operaciones del programa de índice maestro se pueden dividir en cinco programas o
módulos separados que forman una sola tubería.
input.idx | ordenar | pagenums.idx | combine.idx | format.idx
Todos menos uno de los programas están escritos con awk. Para ordenar las entradas,
confiamos en sor t, una utilidad estándar de UNIX. Aquí hay un breve resumen de lo que
hace cada uno de estos programas:
input.idx
Estandariza el formato de las entradas y las rota.
sor t
Ordena las entradas por clave, volumen y número de página.
pa genums.idx
Fusiona entradas con la misma clave, creando una lista de números de página.
combine.idx
Combina números de página consecutivos en un rango.
format.idx
Prepara el índice formateado para la pantalla o el procesamiento por trof f.
Discutiremos cada uno de estos pasos en una sección separada.
Entrada de estandarización
Este script input.idx busca diferentes tipos de entradas y las estandariza para un
procesamiento más fácil por programas posteriores. Además, gira automáticamente las
entradas de índice que contienen una tilde (˜). (Consulte la sección "Rotación de dos
partes" más adelante en este capítulo).
La entrada al programa input.idx consta de dos campos separados por tabuladores, como
se describió anteriormente. El programa produce registros de salida con tres campos
separados por dos puntos. El primer campo contiene la clave primaria; el segundo campo
contiene las claves secundarias y terciarias, si están definidas; y el tercer campo contiene
el número de página.
* Este script de shell y la documentación para el programa se presentan en el Apéndice C. Es posible que desee
leer primero la documentación para una comprensión básica del uso del programa.
296 Capítulo 12: Aplicaciones con todas las
funciones
N. ° 1 Entradas coincidentes que necesitan rotación que contienen una sola tilde
# $ 1 ˜ / ˜ [ˆ˜] / # regexp no funciona y no sé por qué $ 1 ˜ / ˜ / && $ 1! ˜ / ˜˜ / {
# dividir el primer campo en una matriz llamada subcampo
n = split ($ 1, subcampo, "˜")
si (n == 2) {
# imprime la entrada sin "˜" y luego gira
printf ("% s% s ::% s \ n", subcampo [1], subcampo [2], $ 2)
printf ("% s:% s:% s \ n", subcampo [2], subcampo [1], $ 2)
}
siguiente
} # Fin de 1
# 4 limpiar entradas
{
# busque el segundo colon, que podría usarse en lugar de ";" if (sub (/:.*:/, "&;", $ 1)) {
sub (/:; /, ";", $ 1)
}
# elimine el espacio en blanco si lo hay después de los dos puntos.
sub (/: * /, ":", $ 1)
# Si se usa una coma como delimitador, conviértalo a dos puntos. if ($ 1! ˜ /: /) {
} # Fin de 7
# funciones de apoyo
#
# impresorar - mensaje de error de impresión y registro actual
# Arg: mensaje a mostrar
Este script consta de una serie de reglas de coincidencia de patrones para reconocer
diferentes tipos de entrada. Tenga en cuenta que una entrada puede coincidir con más de
una regla a menos que la acción asociada con una regla llame a la siguiente instrucción.
Cuando describamos este script, nos referiremos a las reglas por número. La regla 1 gira
las entradas que contienen una tilde y produce dos registros de salida. La función split ()
crea una matriz llamada subcampo que contiene las dos partes de la entrada de com-
pound. Las dos partes se imprimen en su orden original y luego se intercambian para
crear un segundo registro de salida en el que la clave secundaria se convierte en una clave
principal.
Debido a que estamos usando la tilde como un carácter especial, debemos proporcionar
alguna forma de ingresar realmente una tilde. Hemos implementado la convención de que
dos tildes consecutivas se traducen en una sola tilde. La regla 2 trata ese caso, pero
observe que el patrón para la regla 1 se asegura de que la primera tilde que coincida no
**
sea seguida por otra tilde.
* En la primera edición, Dale escribió: “Para obtener crédito adicional, envíeme un correo electrónico si puede
descubrir por qué la expresión regular comentada justo antes de la regla 1 no funciona. Usé la expresión compuesta
como último recurso ". Me da vergüenza admitir que esto también me dejó perplejo. Cuando Henry Spencer encendió
la luz, fue cegador: “La razón por la cual la expresión regular comentada no funciona es que no hace lo que
pensó el autor. Busca tilde seguido de un carácter que no sea tilde. . . ¡pero la segunda tilde de una
combinación ˜˜ generalmente es seguida por una no tilde! Usar / [ˆ˜] ˜ [ˆ˜] / probablemente funcionaría ”.
Conecté esta expresión regular al programa, y funcionó bien. [ARKANSAS]
Generando un índice enmarañado 299
La regla 3 hace un trabajo similar al de la regla 2; permite que "::" se use para generar un
":" literal en el índice. Sin embargo, dado que usamos los dos puntos como delimitador
de entrada durante toda la entrada al programa, no podemos permitir que aparezca en una
entrada como salida final hasta el final. Por lo tanto, reemplazamos la secuencia "::" con
el valor ASCII del colon en octal. (El programa format.idx revertirá el reemplazo).
En la sintaxis "básica", las claves primaria y secundaria están separadas por dos puntos.
Las teclas secundaria y terciaria están separadas por un punto y coma. No obstante, el
programa también reconoce un segundo colon, en lugar de un punto y coma, como
delimitador entre las teclas secundaria y terciaria. También reconoce que si no se
especifican dos puntos como delimitador, entonces se puede usar una coma como
delimitador entre las teclas primaria y secundaria. (En parte, esto se hizo para ser
compatible con un programa anterior que usaba la coma como delimitador). La función
sub () busca la primera coma en la línea y la cambia a dos puntos. Esta regla también
trata de estandarizar la sintaxis de las entradas "ver" y "ver también". Para las entradas
que están delimitadas por dos puntos, la regla 4 elimina espacios después de los dos
puntos. Todo el trabajo se realiza utilizando la función sub ().
La regla 5 trata de las entradas "ver también". Anteponemos la cadena arbitraria "˜zz" a
las entradas "ver también" para que se ordenen al final de la lista de claves secundarias.
El script pa genums.idx, más adelante en la tubería, eliminará "˜zz" después de que se
hayan ordenado las entradas.
La regla 6 coincide con las entradas que no especifican un número de página. La única
entrada válida sin un número de página contiene una referencia "ver". Esta regla genera
entradas "ver" con ":" al final para indicar un tercer campo vacío. Todas las demás
entradas generan un error o mensaje a través de la función pr interr (). Esta función
notifica al usuario que una entrada particular no tiene un número de página y no se
incluirá en la salida. Este es un método para estandarizar la entrada: mostrar lo que no
puede interpretar correctamente. Sin embargo, es fundamental notificar al usuario para
que pueda corregir la entrada.
La regla 7 genera entradas que contienen el delimitador de dos puntos. Su acción se usa a
continuación para evitar alcanzar la regla 8.
Finalmente, la regla 8 coincide con las entradas que contienen solo una clave primaria.
En otras palabras, no hay delimitador. Generamos "::" para indicar un segundo campo
vacío.
300 Capítulo 12: Aplicaciones con todas las
funciones
Ella es una parte del contenido de nuestro archivo de prueba. Lo usaremos para generar
ejemplos en esta sección.
$ prueba de gato
XView: programas; inicialización 45
XV_INIT_ARGS˜macro 46
Xv_object˜type49
Xv_singlecolor˜type 80
gráficos: (ver también imagen del servidor)
gráficos, modelo XView83
Sistema X Window: eventos 84
gráficos, CANVAS_X_PAINT_WINDOW 86
Sistema X Window, ID de X Window para el kit de herramientas
de paint window 87 (Ver Sistema X Window). gráficos: (ver
también imagen del servidor)
Cada entrada ahora consta de tres campos separados por dos puntos. En la salida de
muestra, puede encontrar ejemplos de entradas con solo una clave primaria, aquellas con
claves primarias y secundarias, y aquellas con claves primarias, secundarias y terciarias.
También puede encontrar ejemplos de entradas rotadas, entradas duplicadas y entradas
"ver también".
La única diferencia en la salida para las entradas multivolumen es que cada entrada
tendría un cuarto campo que contiene el identificador de volumen.
Como puede ver, utilizamos varias opciones con el comando sor t. La primera opción, -b,
especifica que los espacios iniciales se ignoren. La opción -d especifica un tipo de
diccionario en el que se ignoran los símbolos y caracteres especiales. -f especifica que las
letras mayúsculas y minúsculas se deben plegar juntas; en otras palabras, deben ser
tratados como el mismo personaje para propósitos de este tipo. El siguiente argumento es
quizás el más importante: -t: le dice al programa que use dos puntos como delimitador de
campo para ordenar las claves. Las opciones "+" que siguen especifican el número de
campos que se omiten desde el principio de la línea. Por lo tanto, para especificar el
primer campo como la clave de clasificación principal, usamos "+0". Del mismo modo,
las opciones "-" especifican el final de una clave de clasificación. La especificación "-1"
indica que la clave de ordenación primaria termina en el primer campo o al comienzo del
segundo campo. El segundo campo de clasificación es la clave secundaria. El cuarto
campo ("+3") si existe, contiene el número de volumen. La última clave para ordenar es
el número de página; esto requiere un orden numérico (si no dijimos a sor t que esta clave
consiste en números, entonces el número 1 iría seguido de 10, en lugar de 2). Observe
que ordenamos los números de página después de ordenar los números de volumen. Por
lo tanto, todos los números de página para el Volumen I se ordenan en orden antes de los
números de página para el Volumen II. Finalmente, canalizamos la salida a uniq para
eliminar entradas idénticas. Al procesar la salida de input.idx, el comando sor t produce:
entonces el número 1 sería seguido por 10, en lugar de 2). Observe que ordenamos los
números de página después de ordenar los números de volumen. Por lo tanto, todos los
números de página para el Volumen I se ordenan en orden antes de los números de
página para el Volumen II. Finalmente, canalizamos la salida a uniq para eliminar
entradas idénticas. Al procesar la salida de input.idx, el comando sor t produce: entonces
el número 1 sería seguido por 10, en lugar de 2). Observe que ordenamos los números de
página después de ordenar los números de volumen. Por lo tanto, todos los números de
página para el Volumen I se ordenan en orden antes de los números de página para el
Volumen II. Finalmente, canalizamos la salida a uniq para eliminar entradas idénticas. Al
procesar la salida de input.idx, el comando sor t produce:
gráficos: CANVAS_X_PAINT_WINDOW: 86
gráficos: modelo XView: 83
gráficos: ˜zz (ver también imagen del servidor):
archivo de encabezado: Xlib.h: 89
macro: XV_INIT_ARGS: 46
kit de herramientas: (Ver sistema X Window) .:
tipo: Xv_object: 49
tipo: Xv_singlecolor: 80
Sistema X Window: eventos: 84
Sistema X Window: X ID de ventana para ventana de pintura: 87
Xlib: repintar lienzo: 88
Archivo de encabezado Xlib.h :: 89
XView: programas; inicialización: 45
XV_INIT_ARGS macro :: 46
Tipo de objeto Xv :: 49
Xv_singlecolor type :: 80
El cuarto es opcional. Por ahora, consideramos solo el índice de un solo libro, en el que
no hay números de volumen. Recuerde que las entradas ahora están ordenadas.
El corazón de este programa compara la entrada actual con la anterior y determina qué
salida. Los condicionales que implementan la comparación se pueden extraer y expresar
en pseudocódigo, de la siguiente manera:
PRIMARIO = $ 1
SECUNDARIO = $ 2
PÁGINA = $ 3
if (PRIMARIO == prevPRIMARIO)
if (SECUNDARIO == prevSECONDARY)
Imprimir página
más
imprimir PRIMARIO: SECUNDARIO: PÁGINA
más
imprimir PRIMARIO: SECUNDARIO: PÁGINA
prevPRIMARY = PRIMARY
prevSECONDARY = SECUNDARY
Veamos cómo este código maneja una serie de entradas, comenzando con:
XView :: 18
La clave primaria no coincide con la clave primaria anterior; la línea se emite como es:
XView :: 18
Cuando comparamos la clave principal de esta entrada con la anterior, son las mismas.
Cuando comparamos claves secundarias, difieren; sacamos el registro como está:
XView: acerca de: 3
Debido a que las claves primaria y secundaria coinciden con las claves de la entrada
anterior, simplemente mostramos el número de página. (La función pr intf se usa en lugar
de pr int para que no haya una nueva línea automática). Este número de página se agrega
a la entrada anterior para que se vea así:
XView: aproximadamente: 3,7
Nuevamente, solo se muestra el número de página para que la entrada ahora se vea así:
XView: aproximadamente: 3,7,10
De esta manera, tres entradas que difieren solo en el número de página se combinan en
una sola entrada.
El script completo agrega una prueba adicional para ver si el identificador de volumen
coincide.
Ella es el script completo pa genums.idx:
#! / work / bin / nawk -f
# ------------------------------------------------
# pagenums.idx - recolecta páginas para entradas comunes
# Autor: Dale Dougherty
# Versión 1.1 10/07/90
##
# la entrada debe ser PRIMARIA: SECUNDARIA: PÁGINA: VOLUMEN
# ------------------------------------------------
# compruebe si hay un ver también y recójalo en una matriz si (SECUNDARIO ˜ / \ ([Ss] ee + [Aa]
lso /) {
# crear copia tmp y eliminar "˜zz" de la copia tmpSecondary = SECUNDARIO
sub (/ ˜zz \ ([Ss] ee + [Aa] lso * /, "", tmpSecondary) sub (/ \)
* /, "", tmpSecondary)
# eliminar clave secundaria junto con "˜zz"
sub (/ˆ.*˜zz \ ([Ss] ee + [Aa] lso * /, "", SECUNDARIO)
sub (/ \) * /, "", SECUNDARIO)
# asignar al siguiente elemento de seeAlsoList seeAlsoList [++ eachSeeAlso] = SECONDARY
";" prevPrimary = PRIMARY
# asignar copia a la clave secundaria anterior prevSecondary = tmpSecondary next
# Condicionales para comparar claves del registro actual con las anteriores
# grabar. Si las claves primaria y secundaria son iguales, solo
# Se imprime el número de página.
# prueba para ver si cada tecla PRIMARIA coincide con la tecla anterior
if (PRIMARIO == prevPrimary) {
304 Capítulo 12: Aplicaciones con todas las
funciones
# prueba para ver si cada tecla SECUNDARIA coincide con la tecla anterior if (SECONDARY
== prevSecondary)
# prueba para ver si VOLUME coincide;
# imprimir solo VOLUMEN: PÁGINA
if (VOLUME == prevVolume)
printf (",% s", PAGE)
más {
printf (";")
volpage (VOLUMEN, PÁGINA)
}
más{
# if array of See Alsos, imprímalos ahora if (eachSeeAlso) outputSeeAlso (2)
# imprimir PRIMARIO: SECUNDARIO: VOLUMEN: PÁGINA
printf ("\ n% s:% s:", PRIMARIO, SECUNDARIO)
volpage (VOLUMEN, PÁGINA)
}
} # fin de la prueba para PRIMARY == prev
else {# PRIMARY! = prev
# si tenemos una matriz de See Alsos, imprímalos ahora if (eachSeeAlso)
outputSeeAlso (1)
si (NR! = 1)
printf ("\ n") if
(NF == 1) {
printf ("% s:", $ 0)
}
más {
printf ("% s:% s:", PRIMARY, SECONDARY)
volpage (VOLUME, PAGE)
}
}
prevPrimary = PRIMARY
prevSecondary = SECUNDARIO
prevVolume = VOLUME
Recuerde, en primer lugar, que la entrada al programa está ordenada por sus claves. Los
números de página también están en orden, de modo que una entrada para “gráficos” en
la página 7 aparece en la entrada anterior a la de la página 10. De manera similar, las
entradas para el Volumen I aparecen en la entrada antes del Volumen II. Por lo tanto, este
programa no necesita ordenar; simplemente compara las teclas y, si son las mismas,
agrega el número de página a una lista. De esta forma, las entradas se reducen.
Este script también maneja las entradas "ver también". Como los registros ahora están
ordenados, podemos eliminar la secuencia de clasificación especial "˜zz". También
manejamos el caso donde podríamos encontrar entradas consecutivas de "ver también".
No queremos generar:
Toolkit (ver también Xt) (Ver también XView) (Ver también Motivo).
En cambio, nos gustaría combinarlos en una lista para que aparezcan como:
Kit de herramientas (ver también Xt; XView; Motif)
Para hacer eso, creamos una matriz llamada seeAlsoList. Desde SECUNDARIO,
eliminamos los paréntesis, la clave secundaria si existe y el "ver también" y luego lo
asignamos a un elemento de seeAlsoList. Hacemos una copia de SECUNDARY con la
clave secundaria y la asignamos a prevSecondar y para hacer comparaciones con la
siguiente entrada.
PRIMARIO = $ 1
SECUNDARIO = $ 2
PÁGINA = $ 3
if (PRIMARIO == prevPRIMARIO)
imprimir: SECUNDARIO:
más
imprimir PRIMARIO: SECUNDARIO
prevPRIMARY = PRIMARY
prevSECONDARY = SECUNDARY
Si las claves primarias coinciden, solo mostramos la clave secundaria. Por ejemplo, si hay
tres entradas:
XView: 18
XView: aproximadamente: 3, 7, 10
XView: como sistema orientado a objetos: 17
Generando un índice enmarañado 307
saldrán como:
XView: 18
: sobre: 3, 7, 10
: como sistema orientado a objetos: 17
Dejamos caer la clave primaria cuando es la misma. El código real es un poco más difícil
porque hay claves terciarias. Tenemos que probar las claves primarias y secundarias para
ver si son únicas o iguales, pero no tenemos que probar las claves terciarias. (Solo
necesitamos saber que están allí).
Sin duda notó que el pseudocódigo anterior no genera números de página. La segunda
función de este script es examinar los números de página y combinar una lista de
números consecutivos. Los números de página son una lista separada por comas que se
puede cargar en una matriz, utilizando la función split ().
Para ver si los números son consecutivos, recorremos la matriz comparando cada
elemento con 1 + el elemento anterior.
cada página [j-1] +1 == cada página [j]
23-25
El código real parece más complicado que esto porque se llama desde una función que
debe reconocer pares de volumen y número de página. Primero tiene que dividir el
volumen de la lista de números de página y luego puede llamar a la función
(rangeOfPages ()) para procesar la lista de números.
Su e es la lista completa de combine.idx:
#! / work / bin / nawk -f
# ------------------------------------------------
# combine.idx - fusionar claves con la misma clave PRIMARIA
# y combinar números de página consecutivos
# Autor: Dale Dougherty
# Versión 1.1 10/07/90
##
# la entrada debe ser PRIMARIA: SECUNDARIA: PAGELISTA
# ------------------------------------------------
# dividir el segundo campo, obteniendo claves SEC y TERT. sizeOfArray = split ($ 2, array, ";")
SECUNDARY = array [1]
prevPrimary = PRIMARY
}
prevSecondary = SECUNDARIO
} # fin del procedimiento principal
Generando un índice enmarañado 309
# rutina para las entradas "Ver" (solo clave primaria) NF == 1 {printf ("\ n")}
NF> 1 {
si (PAGELISTA)
# llama a la función numrange () para buscar
# Números de página consecutivos.
printf (":% s", rango de números (PAGELIST))
más
if (! isTertiary || (TERCIARIO && SECUNDARIO)) printf (":")
} # final de NF> 1
# El procedimiento END genera una nueva línea END {printf ("\ n")}
# Funciones de apoyo
# Ambos llaman a rangeOfPages en la lista de números de página. if (split (PAGE, volPage, "ˆ")
== 2)
# if VolumeˆPage, separe el volumen y luego llame a rangeOfPages listOfPages =
volPage [1] "ˆ" rangeOfPages (volPage [2])
más # No hay número de volumen involucrado
listOfPages = rangeOfPages (volPage [1])
resultado = listOfPages
} # fin de otra cosa
# hay un rango
páginas = primera página "-" última página
}
sino # no hay rango; solo leer páginas de
primera página = primera página
Generando un índice enmarañado 311
} # fin de sizeOfArray> 1
Este script consta de procedimientos mínimos de INICIO y FIN. La rutina principal hace
el trabajo de comparar claves primarias y secundarias. La primera parte de esta rutina
asigna los campos a las variables. El segundo campo contiene las claves secundarias y
terciarias y usamos split () para separarlas. Luego probamos que hay una clave terciaria y
establecemos el indicador isTer tiary en 1 o 0.
La siguiente parte del procedimiento principal contiene las expresiones condicionales que
buscan claves idénticas. Como dijimos en nuestra discusión sobre el pseudocódigo para
esta parte del programa, pa genums.idx ya ha eliminado las entradas con claves
completamente idénticas.
: secundario: terciario
::terciario
primaria: secundaria: terciaria
El segundo procedimiento trata con todas las entradas que tienen números de página. Este
es el procedimiento donde llamamos a una función para separar la lista de números de
página y buscar páginas consecutivas. Llama a la función numrange (), cuyo objetivo
principal es tratar con un índice multivolumen donde podría verse una lista de números
de página:
Iˆ35,55; IIˆ200
Esta función llama a split () usando un delimitador de punto y coma para separar cada
volumen. Luego llamamos a split () usando un delimitador "ˆ" para separar el número de
volumen de la lista de números de página. Una vez que tenemos la lista de páginas,
llamamos a una segunda función rangeOfPages () para buscar números consecutivos. En
un índice de un solo libro, como el ejemplo que se muestra en este capítulo, la función
numrange () realmente no hace más que llamar a rangeOfPages (). Discutimos la carne de
la función rangeOfPages () anteriormente. Se crea la matriz eachpa ge y se usa un bucle
while para recorrer la matriz y comparar un elemento con el anterior. Esta función
devuelve la lista de páginas.
La salida de muestra de este programa sigue:
Xlib: 6
: repintar lienzo: 88
Archivo de encabezado Xlib.h: 89, 294
Xv_ Tipo de fuente: 310
XView: 18
: sobre: 3, 7, 10
: como sistema orientado a objetos: 17
: compilando programas: 41
: el concepto de ventanas difiere de X: 25
: tipos de datos; tabla de: 20
: ejemplo de interfaz de programación: 44
: cuadros y subtramas: 26
: funciones genéricas: 21
: Objeto genérico: 18, 24
: bibliotecas: 42
: notificación: 10, 35
: objetos: 23-24;
: tabla de: 20;
: lista de: 43
: paquetes: 18, 43: modelo del
programador: 17-23: interfaz de
programación: 41
Generando un índice enmarañado 313
: programas; inicialización: 45
: nombres reservados: 43
: prefijos reservados: 43
: estructura de aplicaciones: 41
: subventanas: 28
: tipos: 43
: objetos de ventana: 25
Formateando el índice
Las secuencias de comandos anteriores han realizado casi todo el procesamiento, dejando
la lista de entradas en buen estado. El script format.idx, probablemente el más fácil de los
scripts, lee la lista de entradas y genera un informe en dos diferen- tes para esteras, una
para visualizar en una pantalla de terminal y otra para enviar a trof f para imprimir en un
láser. impresora. Quizás la única dificultad es que sacamos las entradas agrupadas por
cada letra del alfabeto.
Un argumento de línea de comandos establece la variable FMT que determina cuál de los
dos formatos de salida se utilizará.
Su e es la lista completa de format.idx:
#! / work / bin / nawk -f
# ------------------------------------------------
# format.idx - preparar índice formateado
# Autor: Dale Dougherty
# Versión 1.1 10/07/90
##
# la entrada debe ser PRIMARIA: SECUNDARIA: PÁGINA: VOLUMEN
# Args: formato FMT = 0 (predeterminado) para la pantalla
# FMT = 1 salida con macros troff
# MACDIR = nombre de ruta del archivo de macro de troff de índice
# ------------------------------------------------
EMPEZAR { FS = ":"
upper = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
lower = "abcdefghijklmnopqrstuvwxyz"
}
gsub (/ \\ 72 /, ":", $ 2)
gsub (/ \\ 72 /, ":", $ 3)
char = prevChar
}
# if new char, entonces comience el grupo para la nueva letra del alfabeto if (char! =
prevChar) {
si (FMT == 1)
printf (". XF A \"% s \ "\ n", substr (upper, char, 1))
más
printf ("\ n \ t \ t% s \ n", substr (upper, char, 1))
prevChar = char
}
Generando un índice enmarañado 315
# Función de apoyo
El procedimiento principal comienza convirtiendo los dos puntos "ocultos" en dos puntos
literales. Tenga en cuenta que aplicamos la función gsub () a cada campo en lugar de a la
línea completa porque al hacer esto último se volvería a evaluar la línea y se alteraría el
orden actual de los campos.
Luego asignamos los campos a las variables y luego probamos para ver si el campo está
vacío. Si la clave primaria no está definida, entonces vemos si la clave secundaria está
definida. Si es así, lo sacamos. Si no es así, sacamos una clave terciaria. Si se define la
clave primaria, extraemos su primer carácter y luego vemos si la encontramos en la
cadena inferior.
firstChar = substr ($ 1, 1, 1)
Este resultado debe formatearse con trof f para producir una versión impresa del índice.
El índice de este libro se realizó originalmente utilizando el programa de índice maestro.
para especificar que se cree un índice maestro a partir de los archivos volumen1 y
volumen2 y que la salida se envíe a la pantalla.
El script de shell de índice maestro se presenta en el Apéndice C con la documentación.
318 Capítulo 12: Aplicaciones con todas las
funciones
Esta sección presenta algunos detalles interesantes del programa de índice maestro que de
otro modo podrían pasar desapercibidos. El propósito de esta sección es extraer algunos
fragmentos interesantes del programa y mostrar cómo resuelven un problema en
particular.
Debido a que el programa usa dos puntos como un carácter especial, debemos
proporcionar una manera de pasar dos puntos a través del programa. Para hacer esto,
permitimos que el indexador especifique dos dos puntos consecutivos en la entrada. Sin
embargo, no podemos simplemente convertir la secuencia a dos puntos literales porque el
resto de los módulos del programa llamados por el índice maestro leen tres campos
separados por dos puntos. La solución es convertir el colon a su valor octal usando la
función gsub ().
# <de input.idx
# convertir dos puntos literales a valor octal $ 1 ˜ / :: / {
"\\ 72" representa el valor octal de un colon. (Puede encontrar este valor escaneando una
tabla de equivalentes hexadecimales y octales en el archivo / usr / pub / ascii.) En el
último módulo de programa, usamos gsub () para convertir el valor octal de nuevo a dos
puntos. Aquí está el código de format.idx.
# <de format.idx
# convertir colon octal a colon "literal"
# haga sub para cada campo, no $ 0, para que los campos no se analicen gsub (/ \\ 72 /, ":", $ 1)
Lo primero que observa es que hacemos esta sustitución para cada uno de los tres campos
por separado, en lugar de tener un comando de sustitución que funciona con $ 0. La razón
de esto es que los campos de entrada están separados por dos puntos. Cuando awk
escanea una línea de entrada, divide la línea en campos. Si cambia el contenido de $ 0 en
cualquier punto de la secuencia de comandos, awk volverá a evaluar el valor de $ 0 y
analizará la línea en los campos nuevamente. Por lo tanto, si tiene tres campos antes de
realizar la sustitución, y el
Detalles de repuesto del programa masterindex 319
la sustitución hace un cambio, agregando dos puntos a $ 0, luego awk reconocerá cuatro
campos. Al hacer la sustitución de cada campo, evitamos que la línea se analice
nuevamente en los campos.
La regla de coincidencia de patrones coincide con cualquier entrada que contenga una
tilde pero no dos tildes consecutivas, lo que indica una tilde literal. El procedimiento
utiliza la función split () para dividir el primer campo en dos "subcampos". Esto nos da
dos subcadenas, una antes y otra después de la tilde. Se emite la entrada original y luego
se emite la entrada girada, ambas utilizando la instrucción pr intf.
* La idea de rotar las entradas de índice se derivó del lenguaje de programación AWK. Sin embargo, una entrada
se gira automáticamente donde se encuentra un espacio en blanco; la tilde se usa para evitar una rotación al "llenar" el
espacio. En lugar de que la rotación sea la acción predeterminada, utilizamos una convención de codificación
diferente, donde la tilde indica dónde debe ocurrir la rotación.
320 Capítulo 12: Aplicaciones con todas las
funciones
Debido a que la tilde se usa como un carácter especial, usamos dos tildes consecutivas
para representar una tilde literal en la entrada. El siguiente código aparece en el programa
después del código que intercambia las dos partes de una entrada.
# <de input.idx
# Haga coincidir las entradas que contienen dos tildes $ 1 ˜ / ˜˜ / {
# reemplazar con
gsub (/ ˜˜ /, "˜", $ 1)
}
Encontrar un reemplazo
El siguiente fragmento también proviene de input.idx. El problema era buscar dos puntos
separados por texto y cambiar el segundo punto a punto y coma. Si la línea de entrada
contiene
La primera sustitución coincide con el lapso completo entre dos puntos. Hace un
reemplazo con lo que coincide (&) seguido de un punto y coma. Esta sustitución se
produce dentro de una expresión condicional que evalúa el valor devuelto de la función
sub (). Recuerde, esta función devuelve ns 1 si se realiza una sustitución; no devuelve n la
cadena resultante. En otras palabras, si hacemos la primera sustitución, entonces hacemos
la segunda. La segunda sustitución reemplaza ":;" con ";". Debido a que no podemos
hacer el reemplazo directamente, lo hacemos indirectamente al hacer que el segundo
colon parezca distinto.
Detalles de repuesto del programa masterindex 321
El otro lado es que si el programa input.idx no puede aceptar una entrada, debe
informarlo al usuario y descartar la entrada para que no afecte a los otros programas. El
programa input.idx tiene una función utilizada para informar errores llamada pr interr (),
como se muestra a continuación:
Esto abre una tubería a cat, con la salida estándar de cat redirigida al error estándar o. Si
está utilizando gawk, mawk o Bell Labs awk, podría decir:
printf ("ERROR:% s (% d)% s \ n", mensaje, NR, $ 0)> "/ dev / stderr"
La primera entrada de esta muestra tiene un número de página, mientras que la última no.
Cuando el programa input.idx encuentra una entrada "ver también", verifica si se
proporciona un número de página ($ 2). Si hay uno, genera dos registros, el primero de
los cuales es la entrada sin el número de página y el segundo es una entrada y un número
de página sin la referencia "ver también".
# <input.idx
# si no hay número de página
if ($ 2 == "") {
imprimir $ 0 ":"
siguiente
}
más {
# salida dos entradas:
# print Vea también la entrada sin número de página print $ 1 ":"
# eliminar Ver también
sub (/ * ˜zz \ (ver también. * $ /, "", $ 1)
sub (/; /, "", $ 1)
# imprimir como entrada normal si ($ 1 ˜ /: /)
imprimir $ 1 ":" $ 2
más
imprimir $ 1 "::" $ 2
siguiente
}
El siguiente problema a resolver fue cómo ordenar las entradas en el orden correcto. El
programa de sordos, usando las opciones que le dimos, clasificó las teclas secundarias
para las entradas "ver también" juntas bajo "s". (La opción -d hace que se ignore el
paréntesis). Para cambiar el orden de clasificación, modificamos la clave de clasificación
agregando la secuencia "˜zz" al frente.
# <input.idx
# agregue "˜zz" para ordenar al final
sub (/ \ ([Ss] ee [Aa] lso /, "˜zz (ver también", $ 1)
La tilde no se interpreta por el tipo, pero nos ayuda a identificar la cadena más tarde
cuando la eliminamos. Agregar "˜zz" nos asegura la clasificación al final de la lista de
claves secundarias o terciarias.
Hay una función que genera la lista de entradas "ver también", separando cada una de
ellas con un punto y coma. Por lo tanto, la salida de la entrada "ver también" de pa
genums.idx se ve así:
Otra forma más es hacer algo similar a lo que hicimos para las entradas "ver también".
Debido a que los caracteres especiales se ignoran en el orden, podríamos usar el
programa input.idx pr para convertir una secuencia de cambio de fuente trof f como “\
fB” a “˜˜˜” y “\ fI” a “˜˜ ˜˜ ”o cualquier secuencia de escape conveniente. Esto llevaría la
secuencia a través del programa de sordos sin alterar el orden. (Esta técnica fue utilizada
por Steve Talbott en su guión de indexación original).
El único problema adicional que debe reconocerse en ambos casos es que dos entradas
para el mismo término, una con información de fuente y otra sin ella, se tratarán como
entradas diferentes cuando se compara una con la otra.