Як з’єднати всі рядки разом, який відповідає узор?


12

Я хотів би об'єднати рядки разом лише для рядків, які мають певний візерунок (наприклад ;), однак при використанні g/;/jне працює так, як очікувалося, якщо не викликати пару разів.

Наприклад, наступний вміст:

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

при використанні: :g/;/jвихід:

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

або :g/;/-jдає:

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

аналогічно з: :g/;\_.\{-};/j.

Мій очікуваний вихід:

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

або щось подібне, тому всі рядки, що містять візерунок, з'єднуються разом.

Як цього можна досягти?


3
FWIW, :g/;/jне працює, оскільки це робиться за два проходи: спочатку буфер сканується, потім команда застосовується до відповідних рядків.
romainl

Відповіді:


12

Можливе пояснення проблеми

Я думаю, що причина :g/;/jне працює в тому, що :gкоманда працює з двопрохідним алгоритмом:

  • під час першого проходження він позначає лінії, що містять візерунок ;
  • під час другого проходу він працює на позначених лініях

Під час другого проходу :gприєднується до рядка 1;з рядком, 2;оскільки він 1;був позначений під час першого проходу. Однак я підозрюю (не впевнений) , що він не вступає 1; 2;з , 3;тому що лінія 2;більше не існує, його зміст було об'єднано з лінією , 1;яка вже була оброблена.

Тому :gшукає наступний рядок, який був позначений під час першого пропуску ( 3;), і з'єднує його з наступним ( 4;). Після цього проблема повторюється, вона не може приєднатися 3; 4;до неї, 5;оскільки лінія 4;вже не існує.

Рішення 1 (з vimscript)

Можливо, ви можете викликати функцію кожного разу, коли ;буде знайдено рядок, що містить , щоб перевірити, чи містить попередній рядок крапку з комою:

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

Потім використовуйте таку глобальну команду:

:g/;/call JoinLines()

Або без функції:

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

Рішення 2 (без вимскрипту)

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

Щоразу, коли глобальна команда :gзнаходить шаблон, ;вона виконує команду: .,/^[^;]*$/-1j

Його можна розбити так:

:g/pattern/a,bj

Де:

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

b можна поділити далі так:

/    = 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

jє скороченою формою команди Ex, :joinякій, як і більшості інших команд Ex, може передувати діапазон.
Тут йому передує діапазон: .,/^[^;]*$/-1( a,b)
Діапазон слідує за формою, a,bде aі, bяк правило, 2 номери рядків, і дозволяє оперувати групою рядків, число яких між aі b, а не однією.

Таким чином jкоманда з'єднує всі рядки між поточним ( a) та наступним, який не містить крапки з комою мінус один ( b).

Для отримання додаткової інформації див:

:help :global
:help :join
:help :range

2

Я постійно приєднуюсь до глобального пошуку та заміни:

s /; \ n /; /

\n відповідає новому рядку.

Щоб знайти та видалити порожні рядки:

s / ^ $ \ n //

Я не впевнений, чому, але якщо ви хочете вставити новий рядок, який ви повинні використовувати \r


sпоодинці буде працювати лише один рядок, щоб зробити його глобальним, вам потрібно скористатися %s, але тоді він приєднається майже до всіх рядків, включаючи не ;рядки
kenorb

2
@kenorb Ех ні, я думаю, ви можете використовувати :sкоманду саме для того, що вам потрібно. Я думаю, що це %s/;\n\(.*;\)\@=/;/робить те, що потрібно.
Крістіан Брабандт
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.