web-dev-qa-db-fra.com

Comment trouver tous les motifs entre deux personnages?

J'essaie de trouver tous les modèles entre deux guillemets. Disons que j'ai un fichier dont le contenu se présente comme suit:

first matched is "One". the second is here"Two "
and here are in second line" Three ""Four".

Je veux sous les mots en sortie:

One
Two
Three
Four

Comme vous pouvez le constater, toutes les chaînes en sortie se trouvent entre deux guillemets.

Ce que j'ai essayé, c'est cette commande:

grep -Po ' "\K[^"]*' file

La commande ci-dessus fonctionne correctement si j’ai un espace avant la première paire de marques ". Par exemple, cela fonctionne si mon fichier d'entrée contient les éléments suivants:

first matched is "One". the second is here "Two "
and here are in second line " Three " "Four".

Je sais que je peux le faire avec plusieurs combinaisons de commandes. Mais je cherche une commande sans l’utiliser plusieurs fois. par exemple: sous la commande

grep -oP '"[^"]*"' file | grep -oP '[^"]*'

Comment puis-je obtenir/imprimer tous mes motifs en utilisant une seule commande?

Répondre aux commentaires: Il n'est pas important pour moi de supprimer les espaces autour des motifs correspondants à l'intérieur d'une paire de guillemets, mais il serait préférable que la commande le prenne également en charge. et aussi mes fichiers contiennent des guillemets imbriqués comme "foo "bar" Zoo". Et tous les mots cités sont dans des lignes séparées et ils ne sont pas étendus à plusieurs lignes.

Merci d'avance.

5
αғsнιη

La clé est de consommer les guillemets dans votre expression. Difficile de faire cela avec une seule commande grep. Voici un one-liner Perl:

Perl -0777 -nE 'say for /"(.*?)"/sg' file

Cela masque toute l’entrée et imprime la partie capturée du match. Cela fonctionnera même s'il y a un saut de ligne à l'intérieur des guillemets, même s'il devient alors difficile de séparer les éléments avec et sans saut de ligne. Pour vous aider, utilisez un autre caractère comme séparateur d’enregistrement de sortie, le caractère nul par exemple.

Perl -0777 -lne 'print for /"(.*?)"/sg} BEGIN {$\="\0"' <<DATA | od -c
blah "first" blah "second
quote with newline" blah "third"
DATA
0000000   f   i   r   s   t  \0   s   e   c   o   n   d  \n   q   u   o
0000020   t   e       w   i   t   h       n   e   w   l   i   n   e  \0
0000040   t   h   i   r   d  \0
0000046
2
glenn jackman

Cela pourrait être possible avec la ligne inférieure ci-dessous et j'ai supposé que vous avez des guillemets équilibrés.

grep -oP '"\s*\K[^"]+?(?=\s*"(?:[^"]*"[^"]*")*[^"]*$)' file

Exemple:

$ cat file
first matched is "One". the second is here"Two "
and here are in second line" Three ""Four".
$ grep -oP '"\s*\K[^"]+?(?=\s*"(?:[^"]*"[^"]*")*[^"]*$)' file
One
Two
Three
Four

Une autre solution de coupe de cheveux par le verbe PCRE (*SKIP)(*F) ,

$ grep -oP '[^"]+(?=(?:"[^"]*"[^"]*)*[^"]*$)(*SKIP)(*F)|\s*\K[^"]+(?=\b\s*)' file
One
Two
Three
Four
1
Avinash Raj

Utilisation de sed:

sed 's/[^"]*"\([^"]\+\)"[^"]*/\1\n/g' file

[^"]*

Le ^ au début de [^"]* ... signifie que les caractères listés dans la classe de caractères ne doivent pas correspondre (correspondre uniquement à un "). * signifie que " peut se produire zéro fois ou plus.

"\([^"]\+\)"

Tout ce qui se trouve dans \(...\) est un groupe correspondant. Le premier caractère en dehors du groupe correspondant est la correspondance de début. La classe de caractères [^"] suit (elle correspond à tous les caractères sauf le "). Le quantificateur \+ signifie qu'il doit y avoir au moins un caractère entre les guillemets ("...") dans votre fichier d'entrée. Puis \), la fin du groupe correspondant. Ce groupe de correspondance peut être accessible par son index via \1.

La dernière partie [^"]* est la même que la première partie qui correspond à tout jusqu'au prochain ".

0
αғsнιη

Une approche alternative avec Python qui ne nécessite pas d'expressions régulières (bien que pas tout à fait robuste) consiste à traiter chaque ligne de votre fichier texte caractère par caractère.

Idée de base sur la façon dont cela fonctionne: si nous voyons des guillemets doubles et aucun drapeau levé, levez le drapeau et si nous le revoyons et que le drapeau est levé, abaissez le drapeau. Lorsque le drapeau est levé - c'est comme ça que nous savons que nous sommes entre guillemets doubles, afin que nous puissions stocker les caractères suivants. Une fois le drapeau abaissé, imprimez ce que nous avons lu.

#!/usr/bin/env python
from __future__ import print_function
import sys

flag=False
quoted_string=[]
for line in sys.stdin:
    for char in line.strip():
        if char == '"':
           if flag:
               flag=False
               if quoted_string:
                  print("".join(quoted_string))
                  quoted_string=[]
           else:
               flag=True
               continue 
        if flag:
           quoted_string.append(char)

Et test:

$ cat input.txt
first matched is "One". the second is here"Two "
and here are in second line" Three ""Four".

$ ./get_quoted_words.py < input.txt                                                                                      
One
Two 
 Three 
Four
0
Sergiy Kolodyazhnyy