Analyser un littéral de chaîne Python

9

Le défi est de analyser une chaîne comme Python et à imprimer le contenu de la chaîne.

  • Entrée (argument de ligne de commande ou stdin) : un littéral de chaîne (par exemple "hello") (ou plusieurs littéraux, voir la concaténation de littéral de chaîne ci-dessous)
  • Sortie (sortie standard) : le contenu de la chaîne (par exemple hello)

Règles d'analyse de la chaîne:

  • Un littéral de chaîne est entouré de paires correspondantes de guillemets simples ( 'a'), de guillemets doubles ("a" ), de guillemets simples triples ( '''a''') ou de guillemets doubles triples ( """a"""). La première réapparition du type de guillemets qui a ouvert la chaîne termine la chaîne.
  • La barre oblique inverse s'échappe: \' dans une chaîne devient ', \"devient "et \\devient \. Vous n'avez pas besoin d'implémenter d'autres échappements de barre oblique inverse. Une barre oblique inverse qui ne fait pas partie d'une séquence d'échappement reste une barre oblique inverse.
  • Concaténation de littéraux de chaîne: le contenu des littéraux de chaînes adjacents est concaténé. Par exemple, "hello" 'world'devient helloworld.
  • L'entrée peut contenir des espaces qui ne font partie d'aucun littéral.
  • Vous n'avez pas besoin de prendre en charge tout autre type d'espace, ni à l'intérieur ni à l'extérieur des littéraux.

Règles supplémentaires:

  • eval, execet des éléments similaires ne sont pas autorisés pour analyser le littéral ou des parties de celui-ci.
  • Vous pouvez supposer que l'entrée est valide.
  • Vous pouvez supposer une longueur d'entrée maximale de 1023 caractères.

Exemples:

  • "hello" ' world' -> hello world
  • """\"""'\\\A""" -> """'\\A
  • ( '''"""'''"""'''""" ) (sans parenthèses, mais avec des espaces) -> """'''

Le code le plus court gagne.

tremblement de terre
la source
La sortie doit-elle être sous une forme qui peut être stockée, ou est-ce suffisant pour l'imprimer et en finir avec elle?
DavidC
@David L'impression est tout ce que vous devez faire.
flornquake
Donc (par exemple) "\ z", le code est spécifiquement requis pour sortir la barre oblique inverse et le z? Mais \ 'devient juste une apostrophe, même si elle apparaît entre guillemets doubles ou triples guillemets? Est-ce exact?
boîte à pain le
@breadbox Exactement.
flornquake
Le code doit-il prendre en charge les chaînes brutes? Et qu'en est-il de la concaténation des chaînes non brutes et brutes?
Bakuriu

Réponses:

4

Perl, 54 caractères

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

Juste au moment où je publiais cela, j'ai remarqué qu'elle était presque identique à la solution Ruby de Jan Dvorak. Je suis un peu décontenancé par la similitude, en fait, mais je vais dire "Les grands esprits se ressemblent" et laissez-le faire.

Ce programme met en évidence un cas étrange dans le comptage des caractères dans les scripts Perl: D'après ma lecture, la présence de guillemets simples dans le script signifie que je dois compter l' -poption comme deux caractères dans mon total. Typiquement, lors du calcul des tailles de script Perl, le caractère de tiret initial sur les options est considéré comme libre, sur la justification qu'il peut être groupé avec celui -equi introduit le programme proprement dit ... mais alors vous devez également tenir compte des échappements supplémentaires vous devez entrer le script sur la ligne de commande. Les guillemets simples nécessitent beaucoup d'échappements, donc pour éviter cette pénalité, je dois le compter comme un script exécuté à partir d'un fichier, et donc j'obtiens #!/usr/bin/perlgratuitement, mais pas de caractères d'option. C'est un peu déroutant.

boite à pain
la source
2
Si vous voulez être différent, (('|")\2{2}?)c'est la même longueur que("""|'''|"|')
Peter Taylor
3

C, 178 caractères

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);}

C'est l'une de ces solutions C où tout se fait à l'intérieur d'un gang de chaîne d'opérateurs ternaires.

Le programme fonctionne en copiant les caractères dans le même tampon, en remplaçant les métacaractères. dcontient le délimiteur lorsqu'il est à l'intérieur d'une chaîne et test vrai si le délimiteur est un guillemet triple.

boite à pain
la source
Je pense que vous devez inclure une incrémentation supplémentaire conditionnelle de la variable de contrôle de boucle. Pour 'foo \\' bar ', cela donne foo \ ar', qui ressemble à remplacer \\ par \, mais continue ensuite l'analyse avec le \ fraîchement entré, en voyant le prochain jeton comme \ '.
manatwork
En fait, cet exemple est une entrée non valide. 'foo\\'fait référence à la chaîne foo \, qui est ensuite suivie d'un caractère qui n'est ni un espace ni un délimiteur de chaîne.
boîte à pain le
Oops. J'ai mal lu cette règle. Alors bien sûr, votre code est correct.
manatwork
3

Rubis, 74 73 caractères

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

Le noyau est composé de deux expressions régulières: la première détermine les limites de la chaîne et sélectionne uniquement le contenu. La modification est là pour supprimer tout ce qui n'est pas à l'intérieur des chaînes, et elle supprime également les chaînes non fermées.Les barres obliques inverses sont traitées comme facultatives-facultatives, suivies de tout. Donc,Étant donné que le moteur regex ne reviendra pas en arrière (\\?.)pour des entrées valides (merci @breadbox), une seule barre oblique inverse ne peut pas y correspondre. Les citations sont traitées par répétition paresseuse. Le deuxième regex supprime ensuite une barre oblique inverse avant chaque caractère pouvant être échappé. Le regex dépend du moteur pour toujours choisir l'alternative la plus à gauche en premier.

J'ai également envisagé une approche par machine à états, mais elle s'est avérée assez importante (19 états x 4 classes de caractères) par rapport à la solution regex. Je peux toujours publier la machine d'état si quelqu'un est intéressé.

John Dvorak
la source
Un petit problème avec cette méthode: 'foo \\' bar 'devient foo \ au lieu de' foo \ 'bar'.
manatwork
@manatwork c'est correct, sauf si quelque chose a été perdu dans le formatage. La première barre oblique inverse échappe à la seconde. 'foo\\'est la première chaîne et se bar'trouve en dehors d'un contexte de chaîne lorsque l'entrée est'foo\\'bar'
John Dvorak
Oops. Je ne sais pas comment je l'ai calculé plus tôt. Bien sûr, c'est correct. Désolé.
manatwork
Lorsque j'essaye d'exécuter ceci, j'obtiens un message d'erreur: "imbriqué *? + Dans regexp". Existe-t-il une version minimale ou un indicateur d'exécution dont j'ai besoin?
boîte à pain le
@breadbox Je n'ai pas vérifié d'autres versions, mais j'utilise ruby ​​1.9.3 (JRuby 1.7.2). devrais-je au moins prendre la version 1.9.3 et la modifier?
John Dvorak