Filtrer les paragraphes séparés en fonction de leur premier mot?


3

J'ai un programme qui imprime des lignes de texte ("paragraphes") séparées par "-". Par exemple, il pourrait imprimer

--
are you happy
--
I am hungry
are you
--
are(you hungry
too

Je veux transférer cela dans un autre programme (sed peut-être?) Et ne récupérer que les paragraphes commençant par un mot donné (par exemple, "are"). Donc, dans le cas ci-dessus, obtenir les paragraphes commençant par "sont" en arrière, je serais

--
are you happy
--
are(you hungry
too

Le programme imprime un très grand nombre de «paragraphes», mais je n’en attends qu’un petit nombre. Par conséquent, je préférerais pouvoir filtrer la sortie du programme en continu (en évitant de tout écrire dans un fichier puis le filtrer).

Réponses:


3

AWK

Utilisation de GNU awk ou mawk:

$ awk '$1~"^"word{printf("--\n%s",$0)}' word='are' RS='--\n' infile
--
are you happy
--
are(you hungry
too

Ceci définit le mot variable sur le mot correspondant au début de l'enregistrement et RS (séparateur d'enregistrement) sur '-' suivi d'une nouvelle ligne \n. Ensuite, pour tout enregistrement commençant par le mot à rechercher ( $1~"^"word), imprimez un enregistrement mis en forme. Le format est un "-" de départ avec une nouvelle ligne avec l'enregistrement exact trouvé.

GREP

Utiliser (GNU pour l’ -zoption) grep:

grep -Pz -- '--\nare(?:[^\n]*\n)+?(?=--|\Z)' infile
grep -Pz -- '(?s)--\nare.*?(?=\n--|\Z)\n' infile
grep -Pz -- '(?s)--\nare(?:(?!\n--).)*\n' infile

Description (s) Pour les descriptions suivantes, l'option PCRE (?x)est utilisée pour ajouter (beaucoup) des explications de commentaires (et d'espaces) en ligne avec l'expression rationnelle réelle (de travail). Si les commentaires (et la plupart des espaces) (jusqu'à la nouvelle ligne) sont supprimés, la chaîne résultante reste la même expression régulière. Cela permet de décrire la regex en détail dans le code de travail. Cela facilite grandement la maintenance du code.

Option 1 regex (?x)--\nare(?:[^\n]*\n)+?(?=--|\Z)

(?x)   # match the remainder of the pattern with the following
       # effective flags: x
       #      x modifier: extended. Spaces and text after a # 
       #      in the pattern are ignored
--     # matches the characters -- literally (case sensitive)
\n     # matches a line-feed (newline) character (ASCII 10)
are    # matches the characters are literally (case sensitive)
(?:    #      Non-Capturing Group (?:[^\n]*\n)+?
[^\n]  #           matches non-newline characters
*      #           Quantifier — Matches between zero and unlimited times, as
       #           many times as possible, giving back as needed (greedy)
\n     #           matches a line-feed (newline) character (ASCII 10)
)      #      Close the Non-Capturing Group
+?     # Quantifier — Matches between one and unlimited times, as
       # few times as possible, expanding as needed (lazy)
       # A repeated capturing group will only capture the last iteration.
       # Put a capturing group around the repeated group to capture all
       # iterations or use a non-capturing group instead if you're not
       # interested in the data
(?=    # Positive Lookahead (?=--|\Z)
       # Assert that the Regex below matches
       #      1st Alternative --
--     #           matches the characters -- literally (case sensitive)
|      #      2nd Alternative \Z
\Z     #           \Z asserts position at the end of the string, or before
       #           the line terminator right at the end of the 
       #           string (if any)
)      #      Closing the lookahead.

Option 2 regex (?sx)--\nare.*?(?=\n--|\Z)\n

(?sx)  # match the remainder of the pattern with the following eff. flags: sx
       #        s modifier: single line. Dot matches newline characters
       #        x modifier: extended. Spaces and text after a # in 
       #        the pattern are ignored
--     # matches the characters -- literally (case sensitive)
\n     # matches a line-feed (newline) character (ASCII 10)
are    # matches the characters are literally (case sensitive)
.*?    # matches any character 
       #        Quantifier — Matches between zero and unlimited times,
       #        as few times as possible, expanding as needed (lazy).
(?=    # Positive Lookahead (?=\n--|\Z)
       # Assert that the Regex below matches
       #        1st Alternative \n--
\n     #               matches a line-feed (newline) character (ASCII 10)
--     #               matches the characters -- literally.
|      #        2nd Alternative \Z
\Z     #               \Z asserts position at the end of the string, or
       #               before the line terminator right at
       #               the end of the string (if any)
)      # Close the lookahead parenthesis.
\n     #        matches a line-feed (newline) character (ASCII 10)

Option 3 regex (?xs)--\nare(?:(?!\n--).)*\n

(?xs)  # match the remainder of the pattern with the following eff. flags: xs
       # modifier x : extended. Spaces and text after a # in are ignored
       # modifier s : single line. Dot matches newline characters
--     # matches the characters -- literally (case sensitive)
\n     # matches a line-feed (newline) character (ASCII 10)
are    # matches the characters are literally (case sensitive)
(?:    # Non-capturing group (?:(?!\n--).)
(?!    #      Negative Lookahead (?!\n--)
       #           Assert that the Regex below does not match
\n     #                matches a line-feed (newline) character (ASCII 10)
--     #                matches the characters -- literally
)      #      Close Negative lookahead
.      #      matches any character
)      # Close the Non-Capturing group.
*      # Quantifier — Matches between zero and unlimited times, as many
       # times as possible, giving back as needed (greedy)
\n     # matches a line-feed (newline) character (ASCII 10)

sed

$ sed -nEe 'bend
            :start  ;N;/^--\nare/!b
            :loop   ;/^--$/!{p;n;bloop}
            :end    ;/^--$/bstart'           infile

1

Utiliser GNU awkou mawk:

$ awk -v word="are" -v RS='--\n' -v ORS='--\n' '$1 ~ "^" word "[[:punct:]]?"' file
are you happy
--
are(you hungry
too
--

Ceci définit le séparateur d’enregistrement pour l’entrée et la sortie --suivi d’une nouvelle ligne. Le premier mot de chaque paragraphe se trouve dans $1. Nous comparons cela avec le mot donné (éventuellement suivi d'un caractère de ponctuation). S'ils correspondent, le paragraphe est imprimé.

Notez que les marqueurs de paragraphe dans la sortie seront placés à la fin de chaque paragraphe plutôt qu'au début car nous les utilisons ORSpour les sortir.


Utiliser un sedscript:

:top
/^--/!d;                   # This is not a new paragraph, delete
N;                         # Append next line
/^--\nare[[:punct:]]?/!d;  # This is not a paragraph we want, delete
:record
n;                         # Output line, get next
/^--/!brecord;             # Not yet done with this record, branch to :record
btop;                      # Branch to :top

Fonctionnement:

$ sed -E -f script.sed file
--
are you happy
--
are(you hungry
too

Ou, en tant que ligne unique utilisant la variable shell $word:

sed -E -e ':t;/^--/!d;N;' \
       -e "/^--\n$word[[:punct:]]?/!d" \
       -e ':r;n;/^--/!br;bt' file

Je vous remercie! Y a-t-il un moyen de faire en sorte que le premier mot se termine par la ponctuation? Par exemple, un paragraphe commençant par "êtes-vous" devrait être encore inclus?
LangeHaare

@LangeHaare J'ai vu vos données modifiées et les ai mises à jour.
Kusalananda

@Kusalananda Je m'incline devant votre mentalité AWK supérieure ;-).
Stephen Kitt

1
Votre édition imprime la sortie --à la fin de chaque enregistrement, tandis que la question a --au début de chaque enregistrement. Peut-être que l’utilisation de a printf("--\n%s",$0)est une solution possible (notez que cela n’utilise pas l’OFS et qu’il pourrait rester à la valeur par défaut).
Isaac

@ Isaac je sais. Le séparateur d'enregistrement est par défaut sorti après l'enregistrement, pour le séparer de l'enregistrement suivant. Les données d'entrée utilisent des séparateurs d'enregistrement "humains" en ce sens qu'ils précèdent les enregistrements. En suivant votre recommandation, je dupliquerais plus ou moins votre réponse, ce que je ne veux pas faire. Je travaille également sur une alternative, c'est pourquoi je ne supprime pas celle-ci.
Kusalananda

1

C'est une vieille question, je sais, mais ça me fait mal de voir toute cette boucle, cette ramification et ce motif qui jongle quand un simple

sed '/^--$/!{H;$!d;};x;/^--\nare/!d'

fait la même chose de façon naturelle.

sedest un éditeur de flux linéaire; ainsi, si vous avez besoin de plusieurs lignes, rassemblez ces lignes dans l'espace réservé avec Het sur le paragraphe mark ( ^--$) e xchangez les tampons et testez si le paragraphe doit être imprimé ( ^--\nareune --ligne suivie d'une ligne commençant par are). Le xprécharge déjà l'espace d'attente avec la marque de paragraphe.

Vous n'avez pas besoin d'outils GNU avec des extensions sauvages, vous n'avez pas besoin de compétences en programmation, vous vous contentez de vous engager sed.


0
perl -l -0777ne 'print $& while /^--\nare\b.*?(?=\n(?:--|\z))/msg' inp.file

perl -F'/^--\n/m' -na0777e 'print "--\n$_" for  grep { /^are\b/ } @F' inp.file

sed -e '
 /--/!{H;$!d;s/.*/--/;}
 1d;x;s/.//
 /^are\>/!{s/.*//;h;d;}
 H;s/.*//;x
' inp.file

Je reviendrai plus tard pour des explications de travail ..


0

En examinant votre question, j’ai pensé qu’il devrait être possible de la résoudre à l'aide de grep+ PCRE .

  • La méthode n ° 1 résout ce problème, grâce à @issac, qui l’a aidé à la résoudre.
  • La méthode n ° 2 montre comment utiliser les modificateurs inline ( (?s)) et lookahead ( ?!...).
  • Ma solution initiale (n ° 3) fonctionne bien dans la plupart des situations, à l'exception des types que je souligne ci-dessous dans sa section.

méthode grep n ° 1

$ grep -Pzo -- '--\nare([^\n]*\n)+?(?=--|\Z)' afile

Comment ça fonctionne

commutateurs grep
  • -P - extensions PCRE activées
  • -z- traite l'entrée avec plusieurs lignes, en utilisant NUL à la place de \n(nouvelles lignes)
  • -o - montrer seulement les matches
regex
  • --\nare([^\n]*\n)+?(?=--|\Z)
    • correspond aux doubles tirets suivis d'un are, puis d'une continuation de zéro ou de plusieurs caractères autres que la nouvelle ligne - ou - une nouvelle ligne.
    • Le +?match correspondra à 1 ou plus, mais est non-gourmand, donc il ne continuera pas agressivement.
    • Enfin, les (?=--|\Z)gardes de la fin du bloc recherchent le prochain double tiret --ou la fin du fichier ( \Z).

méthode grep # 2

Cette méthode utilise le modificateur DOTALL inline pour faire .correspondre les nouvelles lignes (`n`).

$ grep -Pzo -- '(?s)--\nare((?!\n--).)+\n' afile

Comment ça fonctionne

commutateurs grep
  • -P - extensions PCRE activées
  • -z- traite l'entrée avec plusieurs lignes, en utilisant NUL à la place de \n(nouvelles lignes)
  • -o - montrer seulement les matches
regex
  • (?s) - Modificateur en ligne DOTALL - Tous les points doivent correspondre à la nouvelle ligne
  • --\nare - correspond à une nouvelle ligne suivie de are
  • ((?!\n--).)+\n- fait correspondre les caractères .tant que le lookahead (?!\n--)ne rencontre pas un \n--. Ce bloc entier de correspondance doit être d' au moins un ou plusieurs ( +) et suivi d'un saut de ligne, \n.

méthode grep n ° 3 (d'origine)

Voici une grepsolution qui utilise l'extension PCRE ( -P). Cette méthode a fonctionné pour tous les exemples fournis, elle échouerait avec des exemples comme celui-ci:

--
are
some-other-dasher

Mais sinon, dans la plupart des cas, je pourrais concevoir de devoir lutter.

$ grep -Pzo -- '--\nare[^\r\n]+[^-]+' afile
--
are you happy

--
are(you hungry
too

Comment ça fonctionne

commutateurs grep
  • -P - extensions PCRE activées
  • -z- traite l'entrée avec plusieurs lignes, en utilisant NUL à la place de \n(nouvelles lignes)
  • -o - montrer seulement les matches
regex
  • '--\nare[^\r\n]+[^-]+'
    • correspond aux doubles tirets suivis d'une nouvelle ligne et du mot are.
    • Il continuera ensuite à imprimer le reste de la ligne areallumée jusqu'à ce qu'une nouvelle ligne soit rencontrée.
    • Il imprime ensuite des caractères jusqu'à ce qu'une séquence de tirets soit rencontrée.

Références


Utilisez un disque avec une première ligne de are(you hungryet une seconde ligne detoo-maybe?
Isaac

@ Isac - si proche, j'ai essayé les tirets sur les arelignes mais je n'en avais pas inséré un dans les lignes qui l'ont suivi. Le ?ne pas vraiment entrer en jeu, vous êtes juste jeter les caractères supplémentaires pour le rendre plus compliqué?
slm

Non @slm, en aucun cas essayer de confondre ou quoi que ce soit, c'est juste que la phrase est une question, il commence par un Are:-) .....
Isaac

@ Isaacac - Gotcha. Ne peut pas arriver avec une regex pour contrecarrer votre tiret dans les lignes secondaires. Je me sentais comme un lookahead devrait être capable de traiter avec mais je ne pouvais pas trouver un qui a fonctionné ici Impossible de trouver quoi que ce soit qui a cassé la awksolution que vous avez présentée, soit 8- (.
slm

Peut-être que: grep -Pzo -- '--\nare([^\n]*\n)+?(?=--|\Z)' infile:-P
Isaac
En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.