Desintercalar líneas de registro: MODO DIFÍCIL

Aug 24 2020

Frente a algunas reglas lamentables que convirtieron el original en una tarea de clasificación glorificada, estoy publicando una variante más desafiante. Grita a Luis Mendo por la sugerencia de cómo mejorar el desafío original.


Ha heredado un servidor que ejecuta varias aplicaciones que generan el mismo registro.

Su tarea consiste en desintercalar las líneas del archivo de registro por fuente. Afortunadamente para ti, la persona que escribió todas las aplicaciones fue lo suficientemente amable como para dejar etiquetas indicando su origen.

Registros

Cada línea se verá así:

[app_name] Something horrible happened!
  • Las etiquetas de las aplicaciones están siempre entre corchetes y solo contendrán caracteres alfanuméricos y guiones bajos.
  • Las etiquetas de la aplicación no están vacías
  • Puede haber otros corchetes más adelante en cualquier línea determinada, pero ninguno formará una etiqueta válida.
  • Siempre habrá al menos un carácter sin espacio después de una etiqueta.
  • El registro en su conjunto puede estar vacío.
  • No hay límite para la cantidad de etiquetas de aplicaciones únicas que estarán presentes en el archivo.

En algunos casos, es posible que falte una etiqueta de aplicación. Cuando este es el caso, la línea de registro pertenece a la aplicación registrada más recientemente.

  • La primera línea del registro siempre comenzará con una etiqueta de aplicación.
  • Una línea que comienza con [no está necesariamente etiquetada. Si hay un carácter no válido entre los corchetes iniciales o no ], entonces la línea no está etiquetada.
  • No aparecen líneas vacías en el registro

Rendimiento esperado

Debe generar varios registros completamente separados con las etiquetas de la aplicación eliminadas de cada línea de registro donde estaban presentes. No es necesario conservar los espacios en blanco iniciales en ninguna línea de registro.

Los registros de salida deben estar en algún tipo de asignación de valor-clave o equivalente razonable. Una lista no exhaustiva de formatos de salida válidos:

  • Un archivo con el nombre de la etiqueta de la aplicación para cada aplicación
    • Puede suponer que los archivos de salida no existen ya en el directorio de salida en este caso.
  • Un diccionario / mapa / hash / lo que sea que use etiquetas de aplicaciones como claves y una cadena separada por saltos de línea de las líneas de registro como valores.
  • Una cadena larga concatenada separada por líneas en blanco y precedida por etiquetas de aplicación
  • Una lista de listas de [clave, valor]
  • Una cadena JSON con etiquetas de aplicación como claves y matrices de líneas de registro como valores
  • Un documento de Markdown con etiquetas de aplicación como encabezados y iniciales #de cualquier línea se escaparon con barras invertidas.
  • Una función de Javascript que toma una cadena como entrada y genera el registro asociado como una cadena separada por saltos de línea.

Básicamente, si no puede saber de qué aplicación provienen las líneas de registro, la salida no es válida.

Ejemplo

Un registro completo podría verse así:

[weather] Current temp: 83F
[barkeep] Fish enters bar
Fish orders beer
[stockmarket] PI +3.14
[PI announced merger with E]
[barkeep] Fish leaves bar
[weather] 40% chance of rain detected
[ I have a lovely bunch of coconuts

Que debería generar tres registros diferentes:

clima:

Current temp: 83F
40% chance of rain detected
[ I have a lovely bunch of coconuts

barman:

Fish enters bar
Fish orders beer
Fish leaves bar

bolsa de Valores:

PI +3.14
[PI announced merger with E]

No se le dan los nombres de las etiquetas de la aplicación con anticipación. Debe determinarlos solo analizando el archivo de registro.

Reglas y puntuación

  • Esto es code-golf , así que gana el código más corto.
  • Se aplican reglas estándar y lagunas
  • Utilice cualquier formato de E / S conveniente, siempre que cada línea de entrada esté representada como una cadena, no como una etiqueta + mensaje previamente analizado. El análisis es parte de este desafío .
  • Las líneas de registro de salida de cada aplicación deben aparecer en el mismo orden en que aparecían en el registro original.
  • Puede suponer que el registro de entrada contiene solo caracteres ASCII.

Respuestas

2 water_ghosts Aug 24 2020 at 21:39

Python 3.8 , 95 bytes

import re
lambda x:[((t:=re.match(r'\[(\w*)\]',s)or t)[1],s.split(t[0])[-1].strip())for s in x]

¡Pruébelo en línea!

(Ejemplo de TIO ampliado con entrada)

Explicación:

Se requiere Python 3.8 para el :=operador. Esto toma una lista de cadenas como entrada y genera una lista de (tag, body)tuplas. Primero, usa una coincidencia de Regex para obtener la etiqueta:

t:=re.match(r'\[(\w*)\]',s)or t)

Esto coincide con cualquier secuencia inicial de caracteres de palabras (alfanuméricos + guión bajo) entre corchetes, con las palabras como grupo de captura. Si la cadena coincide con esta expresión regular, tserá un matchobjeto con dos elementos: la coincidencia completa y el grupo. Por ejemplo, si la cadena es [tag] body, matchtendrá los elementos [tag]y tag.

Si la cadena no coincide con esta expresión regular, re.match()devuelve None. El código se convierte en t = None or t, que es justo t = t, por lo que la etiqueta mantiene su valor de la línea anterior. Si la primera línea no coincide, esto provocaría un error, ¡pero no tenemos que preocuparnos por eso!

El código luego construye la tupla t[1], s.split(t[0])[-1].strip(), donde t[1]está el grupo de captura (la etiqueta sin corchetes) y t[0]la etiqueta con corchetes. Dividir la cadena en la etiqueta completa aísla el cuerpo, ya sea que la etiqueta exista o no en la cadena.

2 Neil Aug 24 2020 at 22:32

Retina 0.8.2 , 95 bytes

+m`^(\[\w+] ).*¶(?!\[\w+])
$&$1
O$`(\w+).*
$1
¶
¶¶
rm`(?<=^\1.*¶)¶(.\w+].)

(?<=(^|¶¶).\w+]).
¶

¡Pruébelo en línea! Explicación:

+m`^(\[\w+] ).*¶(?!\[\w+])
$&$1

Etiquete todas las líneas sin etiquetar.

O$`(\w+).*
$1

Ordena las líneas, tomadas de mi respuesta al desafío original.

¶
¶¶

Haz doble espacio en las líneas.

rm`(?<=^\1.*¶)¶(.\w+].)

Elimine las etiquetas duplicadas y la línea vacía antes de ellas. Esto significa que las únicas líneas vacías que quedan son las que delimitan las etiquetas separadas.

(?<=(^|¶¶).\w+]).
¶

Mueva la etiqueta a su propia línea.

2 Abigail Sep 01 2020 at 14:43

perl -Mfeature = say -n, 47 46 bytes

(Guardado un byte por cortesía de @Dom Hastings)

$;=$1 if s/^\[(\w+)\] +//;$;{$;}.=$_}{say for%;

¡Pruébelo en línea!

¿Como funciona esto?

Primero, el efecto del -ncambio. Esto hace que Perl envuelva el programa en un bucle, que lee la entrada y ejecuta el cuerpo de cada línea. Pero lo hace de una manera muy poco sofisticada, envuelve el cuerpo en el bucle antes de realizar cualquier análisis, de la siguiente manera:

perl -ne 'TEXT'

se convierte en

LINE: while (defined($_ = readline ARGV)) {
    TEXT;
}

Pero eso significa que si tu eres TEXTde la forma LOOP_BODY}{FINAL_STATEMENT, terminas con el programa:

LINE: while (defined($_ = readline ARGV)) {
    LOOP_BODY
}
{
    FINAL_STATEMENT;
}

Estamos usando este truco solo para ahorrar unos pocos bytes en un ENDbloque.

En el programa mismo, estamos usando dos variables para hacer nuestra contabilidad. $;contendrá la etiqueta actual y, en el hash %;, hacemos un seguimiento de las líneas de cada etiqueta. Ahora, para cada línea de la entrada, verificamos si comienza con una etiqueta, y si es así, la quitamos de la línea y recordamos la etiqueta:

$; = $1 if          # Remember the tag if,
s/^\[(\w+)\] +//;   # we can strip of a tag

Luego concatenamos la línea actual (despojada de una etiqueta, si está presente) al conjunto de líneas ya recopiladas para esa etiqueta; si no existen tales líneas, la concatenamos de manera efectiva con la cadena vacía:

$;{$;}.=$_   # Remember the current line

Finalmente, después de leer todas las líneas, imprimimos el hash. Perl aplana convenientemente un hash a una lista simple si lo trata como una lista, alternando las claves y los valores. Esto nos da una salida donde cada sección está separada por una nueva línea y está encabezada por la etiqueta.

say for%;    # Print the flattened hash
1 KevinCruijssen Aug 24 2020 at 17:18

05AB1E , 22 bytes

vyD']¡н¦DžjÃÊi‚»]).¡#н

Ingrese como una lista de líneas, salida como una lista de listas de cadenas de varias líneas.

Pruébelo en línea (bastante impreso; siéntase libre de quitar el pie de página para ver el resultado real).

Explicación:

v                 # Loop `y` over each string of the (implicit) input-list:
 yD               #  Push line `y` twice
   ']¡           '#  Split the copy on "]"
      н           #  Only leave the first part
       ¦          #  Remove the leading character (the potential "[")
        D         #  Duplicate it
         žj       #  Push builtin string "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_"
           Ã      #  Only keep those characters in the string we duplicated
            Êi    #  If it is NOT equal to the string:
              ‚   #   Pair it with the previous line
               »  #   And join that pair with a newline delimiter
]                 # Close both the if-statement and loop
 )                # Wrap all values on the stack into a list
  .¡              # Group all strings by:
    #             #  Split the string on spaces
     н            #  And only leave the first part (the tag)
                  # (after which the result is output implicitly)
1 Noodle9 Aug 24 2020 at 18:21

AWK-F] , 122 123 113 bytes

Se agregó un byte para corregir un error amablemente señalado por water_ghosts .

10 bytes guardados gracias a Giuseppe !!!

/^\[\w+\]/{a[l=$1][i++]=$2;next}{a[l][i++]=$0}END{for(k in a){print"\n",substr(k,2);for(j in a[k])print a[k][j]}}

¡Pruébelo en línea!

1 IsmaelMiguel Aug 25 2020 at 10:23

SimpleTemplate, 142 bytes

Bueno, esto no fue tan difícil.

Esta respuesta es una versión ligeramente modificada de: Desintercalar líneas de registro

{@callexplode intoL EOL,argv.0}{@eachL}{@if_ matches"@^(\[\w+\]) ?(.+)$@"M}{@setX"#{M.1} "}{@set_ M.2}{@/}{@setS.[X]S.[X],X,_,EOL}{@/}{@echoS}

Sin golf:

Dado que esto es bastante ilegible, a continuación se muestra una versión legible:

{@call explode into lines EOL, argv.0}
{@set storage null}
{@each lines as line}
    {@if line matches "@^(\[\w+\]) ?(.+)$@" match}
        {@set last "#{match.1} "}
        {@set line match.2}
    {@/}
    {@set storage.[last] storage.[last], last, line, EOL}
{@/}
{@echo storage}

Cambios:

Se tuvieron que hacer algunos cambios para que funcionara correctamente, con los nuevos requisitos. A continuación se muestra una copia de la respuesta vinculada:

{@call explode into lines EOL, argv.0}
{@set storage null}
{@each lines as line}
    {@if line matches "@^(\[.*\])@" match}
        {@set storage.[match.1] storage.[match.1], line, EOL}
    {@/}
{@/}
{@echo storage}

A continuación se muestra una lista completa de los cambios:

  • La expresión regular se cambió para que coincida con el contenido restante, sin un espacio, si está presente. (Citando: "Siempre habrá al menos un carácter sin espacio después de una etiqueta").
  • Almacena la "aplicación" con un espacio extra, para su uso posterior y para normalizar las líneas (que pueden tener o no un espacio justo después de la "etiqueta").
  • Almacena el contenido restante, sin el primer espacio, en la variable line( _para la versión de golf)
  • Agrega la "etiqueta" antes de la linevariable, que solía ser parte de la linevariable.

Como puede ver, los cambios no son tan significativos. Mueva código, agregue espacio adicional, agregue una variable a una salida.


Puedes probar esto en: http://sandbox.onlinephpfunctions.com/code/eb5380ba1826530087fd92fa71d709c0b2d6de39

user Aug 25 2020 at 00:51

Scala, 127 bytes

l=>((("",List[(String,String)]())/:l){case((p,m),s"[$t] $b")=>(t,(t,b)::m)case((p,m),b)=>(p,(p,b)::m)})._2.groupMap(_._1)(_._2)

Pruébelo en Scastie (no funciona en TIO)

Vaya, esto es largo.