Analizar un literal de cadena de Python

9

El desafío es analizar una cadena como lo hace Python e imprimir el contenido de la cadena.

  • Entrada (argumento de línea de comando o stdin) : un literal de cadena (p "hello". Ej. ) (O múltiples literales, consulte la concatenación de literal de cadena a continuación)
  • Salida (stdout) : el contenido de la cadena (por ejemplo hello)

Reglas para analizar la cadena:

  • Un literal de cadena se encierra en pares coincidentes de comillas simples ( 'a'), comillas dobles ( "a"), comillas simples triples ( '''a''') o comillas dobles triples ( """a"""). La primera recurrencia del tipo de comillas que abrió la cadena termina la cadena.
  • La barra invertida se escapa: \' dentro de una cadena se convierte ', se \"convierte "y se \\convierte \. No necesita implementar ningún otro escape de barra diagonal inversa. Una barra invertida que no forma parte de una secuencia de escape sigue siendo una barra invertida.
  • Concatenación de cadenas literales: el contenido de las cadenas literales adyacentes se concatena. Por ejemplo, se "hello" 'world'convierte helloworld.
  • La entrada puede contener espacios que no forman parte de ningún literal.
  • No necesita admitir ningún otro tipo de espacio en blanco, ni dentro ni fuera de los literales.

Reglas adicionales:

  • eval, execy cosas similares no están permitidas para analizar el literal o partes de él.
  • Puede suponer que la entrada es válida.
  • Puede asumir una longitud de entrada máxima de 1023 caracteres.

Ejemplos:

  • "hello" ' world' -> hello world
  • """\"""'\\\A""" -> """'\\A
  • ( '''"""'''"""'''""" ) (sin paréntesis, pero con espacios) -> """'''

El código más corto gana.

flornquake
fuente
¿El resultado debe ser de una forma que pueda almacenarse, o es suficiente para imprimirlo y terminar con él?
DavidC
@David Printing es todo lo que necesitas hacer.
flornquake
Entonces, en (por ejemplo) "\ z", ¿se requiere específicamente el código para generar la barra diagonal inversa y la z? ¿Pero \ 'se convierte en un apóstrofe, incluso si aparece entre comillas dobles o comillas triples? ¿Es eso correcto?
breadbox
@breadbox Exactamente.
flornquake
¿Debería el código soportar cadenas sin procesar? ¿Y qué pasa con la concatenación de cadenas sin procesar y sin procesar?
Bakuriu

Respuestas:

4

Perl, 54 caracteres

#!/usr/bin/perl -p
s/ |("""|'''|"|')((\\?.)*?)\1/$2/g;s/\\(["'\\])/$1/g

Justo cuando estaba publicando esto, noté que es casi idéntico a la solución Ruby de Jan Dvorak. Estoy un poco molesto por lo similar que es, de hecho, pero voy a decir "Las grandes mentes piensan igual" y lo dejaré pasar.

Este programa destaca un caso extraño en el recuento de caracteres en los guiones de Perl: según mi lectura, la presencia de comillas simples en el guión significa que necesito contar la -popción como dos caracteres para mi total. Por lo general, cuando se calculan los tamaños de script de Perl, el carácter de guión inicial en las opciones se considera libre, con la justificación de que se puede combinar con el -eque introduce el programa propiamente dicho ... pero también debe tener en cuenta cualquier escape adicional. debe ingresar el script en la línea de comandos. Las comillas simples requieren mucho escape, por lo que para evitar esa penalización tengo que contarlo como un script ejecutado desde un archivo, y por lo tanto obtengo los caracteres #!/usr/bin/perlde forma gratuita, pero no cualquier opción. Es un poco confuso.

caja de pan
fuente
2
Si quieres ser diferente, (('|")\2{2}?)tiene la misma longitud que("""|'''|"|')
Peter Taylor
3

C, 178 caracteres

char*p,*q,b[1024];d;main(t){for(p=q=gets(b);*p=*q++;)
d?*p==92&!(*q-*p&&*q-34&&*q-39)?*p++=*q++:*p-d||t&&*q-d|q[1]-d?++p:
(d=0,q+=2*t):*p-32?d=*p,t=*q==d&q[1]==d,q+=2*t:0;puts(b);}

Esta es una de esas soluciones C donde todo se hace dentro de una cadena de operadores ternarios.

El programa funciona copiando los caracteres nuevamente en el mismo búfer, sobrescribiendo los metacaracteres. dmantiene el delimitador cuando está dentro de una cadena, y tes cierto si el delimitador es una comilla triple.

caja de pan
fuente
Creo que debe incluir un incremento adicional condicional de la variable de control de bucle. Para 'foo \\' bar ', da foo \ ar', que parece que reemplaza \\ con \, pero luego continúa el análisis con el recién ingresado \, viendo el siguiente token como \ '.
manatwork
En realidad, ese ejemplo es una entrada no válida. 'foo\\'se refiere a la cadena foo \, que luego es seguida por un carácter que no es un espacio en blanco ni un delimitador de cadena.
breadbox
Ups Leí mal esa regla. Entonces, por supuesto, su código es correcto.
manatwork
3

Rubí, 74 73 caracteres

puts gets.gsub(/('''|"""|'|")((\\?.)*?)\1|./,'\2').gsub /\\([\\'"])/,'\1'

El núcleo aquí son dos expresiones regulares: la primera determina los límites de la cadena y selecciona solo el contenido. La alteración está ahí para eliminar todo lo que no está dentro de las cadenas, y también deja caer las cadenas no cerradas.Las barras invertidas se tratan como posesivas-opcionales seguidas de cualquier cosa. Así,Dado que el motor regex no retrocederá (\\?.)para entradas válidas (gracias @breadbox), una única barra invertida no puede coincidir allí. Las cotizaciones se manejan a través de la repetición perezosa. La segunda expresión regular elimina una barra invertida antes de cada personaje que se puede escapar. La expresión regular depende del motor para elegir siempre la alternativa más a la izquierda primero.

También he considerado un enfoque de máquina de estado, pero resultó bastante grande (19 estados x 4 clases de caracteres) en comparación con la solución regex. Todavía puedo publicar la máquina de estado si alguien está interesado.

John Dvorak
fuente
Una falla menor con este método: 'foo \\' bar 'se convierte en foo \ en lugar de' foo \ 'bar'.
manatwork
@manatwork esto es correcto, a menos que se haya perdido algo en el formateo. La primera barra diagonal inversa escapa a la segunda. 'foo\\'es la primera cadena y bar'está fuera de un contexto de cadena cuando la entrada es'foo\\'bar'
John Dvorak
Ups No tengo idea de cómo lo calculé antes. Por supuesto que es correcto. Lo siento.
manatwork
Cuando intento ejecutar esto, aparece un mensaje de error: "anidado *? + En regexp". ¿Hay alguna versión mínima o indicador de tiempo de ejecución que necesito?
breadbox
@breadbox No he comprobado otras versiones, pero estoy ejecutando ruby ​​1.9.3 (JRuby 1.7.2). ¿Debo asumir al menos 1.9.3 y editar eso en?
John Dvorak