Comment joindre toutes les lignes avec quel motif correspondant?

11

Je voudrais joindre des lignes ensemble uniquement pour les lignes qui ont un certain modèle (comme ;), mais lors de l'utilisation, g/;/jcela ne fonctionne pas comme prévu, sauf si appelé deux fois.

Par exemple le contenu suivant:

a
1;
2;
3;
4;
5;
b
6;
7;
8;
9;
c

lors de l'utilisation: :g/;/jla sortie est:

a
1; 2;
3; 4;
5; b
6; 7;
8; 9;
c

ou :g/;/-jdonne:

a 1; 2; 3; 4; 5;
b 6; 7; 8; 9;
c

similaire avec: :g/;\_.\{-};/j.

Ma sortie attendue est:

a 
1; 2; 3; 4; 5;
b
6; 7; 8; 9;
c

ou quelque chose de similaire, donc toutes les lignes contenant le motif sont jointes.

Comment cela peut-il être réalisé?

Kenorb
la source
3
FWIW, :g/;/jne fonctionne pas car il se fait en deux passes: d'abord le tampon est analysé, puis la commande est appliquée aux lignes correspondantes.
romainl

Réponses:

12

Explication possible du problème

Je pense que la raison pour laquelle cela :g/;/jne fonctionne pas est que la :gcommande fonctionne avec un algorithme à 2 passes:

  • lors du premier passage il marque les lignes contenant le motif ;
  • lors de la deuxième passe il opère sur les lignes marquées

Lors du deuxième passage, :grejoint la ligne 1;avec la ligne 2;car a 1;été marqué lors du premier passage. Cependant je soupçonne (pas sûr) qu'il ne se joint pas 1; 2;à 3;parce que la ligne 2;n'existe plus, son contenu a été fusionné avec la ligne 1;qui a déjà été traitée.

:gRecherche donc la ligne suivante qui a été marquée lors du premier passage ( 3;) et la joint à la suivante ( 4;). Après que les répétitions de problème, il ne peut pas se joindre 3; 4;à 5;parce que la ligne 4;n'existe plus.

Solution 1 (avec vimscript)

Vous pourriez peut-être appeler une fonction chaque fois qu'une ligne contenant ;est trouvée pour vérifier si la ligne précédente contient également un point-virgule:

function! JoinLines()
    if getline(line('.')-1) =~ ';'
        .-1join
    endif
endfunction

Utilisez ensuite la commande globale suivante:

:g/;/call JoinLines()

Ou sans fonction:

:g/;/if getline(line('.')-1) =~ ';' | -j | endif

Solution 2 (sans vimscript)

:g/;/.,/^[^;]*$/-1j

Chaque fois que la commande globale :gtrouve le modèle, ;elle exécute la commande: .,/^[^;]*$/-1j

Il peut être décomposé comme ceci:

:g/pattern/a,bj

Où :

pattern = ;
a       = .           = number of current line
b       = /^[^;]*$/-1 = number of next line without any semicolon minus one

b peut être décomposé comme ceci:

/    = look for the number of the next line matching the following pattern
^    = a beginning of line
[^;] = then any character except a semicolon
 *   = the last character can be repeated 0 or more times
 $   = an end of line
 /   = end of pattern
 -1  = removes one to the number you just got

jest la forme abrégée de la commande Ex :joinqui, comme la plupart des autres commandes Ex, peut être précédée d'une plage.
Ici, elle est précédée de la plage: .,/^[^;]*$/-1( a,b)
Une plage suit la forme a,baet bsont généralement 2 numéros de ligne, et vous permet d'opérer sur un groupe de lignes dont le nombre est compris entre aet b, au lieu d'une seule.

La jcommande joint donc toutes les lignes entre la ligne courante ( a) et la suivante qui ne contient pas de point-virgule moins une ( b).

Pour plus d'informations, voir:

:help :global
:help :join
:help :range
saginaw
la source
1

Je fais de même en me joignant tout le temps avec une recherche globale et je remplace:

s /; \ n /; /

\n correspond à la nouvelle ligne.

Pour rechercher et supprimer des lignes vides:

s / ^ $ \ n //

Je ne sais pas pourquoi, mais si vous voulez insérer une nouvelle ligne, vous devez utiliser \r

rlh100
la source
sseul ne fonctionnera que pour une seule ligne, pour le rendre global, vous devez l'utiliser %s, mais il rejoindra presque toutes les lignes, y compris les non ;lignes
kenorb
2
@kenorb Ehm non, je pense que vous pouvez utiliser la :scommande exactement pour ce que vous voulez. Je pense que cela %s/;\n\(.*;\)\@=/;/fait ce dont vous avez besoin.
Christian Brabandt