RegEx для удаления повторного начала строки с помощью TextWrangler

Попытка превратить

a: 1, 2, 3
a: a, b, v
b: 5, 6, 7
b: 10, 1543, 1345
b: e, fe, sdf
cd: asdf, asdfas dfasdfa,asdfasdfa,afdsfa sdf
e1: asdfas, dafasd, adsf, asdfasd
e1: 1, 3, 2
e1: 9, 8, 7, 6

в

a: 1, 2, 3
   a, b, v
b: 5, 6, 7
   10, 1543, 1345
   e, fe, sdf
cd: asdf, asdfas dfasdfa,asdfasdfa,afdsfa sdf
e1: asdfas, dafasd, adsf, asdfasd
    1, 3, 2
    9, 8, 7, 6

Итак, строки сортируются. Если последовательные строки начинаются с одной и той же последовательности символов до/включая некоторый разделитель (здесь двоеточие (и пробел после него)), должен сохраняться только первый экземпляр - как и остальная часть всех строк. Там может быть до дюжины (с половиной) строк, начинающихся с идентичной последовательности символов. Вход содержит около 4500 строк...

Пробовал в TextWrangler.

Пока шаблон поиска

^([[:alnum:]]+): (.+)\r((\1:) (.+)\r)*

соответствует правильно, ни замена

\1:\t\2\r\t\3\r

ни

\1:\t\2\r\t\4\r

достает меня где-нибудь рядом с тем, что я ищу.

Шаблон поиска

^(.+): (.+)\r((?<=\1:) (.+)\r)*

отклоняется, если lookbehind не является фиксированной длиной. - Не уверен, что все идет в правильном направлении.

Глядя на Как объединить строки, которые начинаются с тех же элементов в текстовом файле Интересно, есть ли вообще элегантный (скажем: один шаблон поиска, одна замена, запуск один раз).

С другой стороны, я просто не могу найти правильный вопрос для поиска в сети. Если вы знаете лучше, пожалуйста, укажите мне в правильном направлении.

Сохранение остатка выравниваемых строк - это, конечно, сахар на торте...

Спасибо за ваше время.

Ответы

Ответ 1

Как обход для переменной длины lookbehind: PCRE допускает альтернативы переменной длины

PCRE не полностью совместима с Perl, когда дело доходит до lookbehind. Хотя Perl требует, чтобы альтернативы внутри lookbehind имели одинаковую длину, PCRE допускает альтернативы переменной длины.

Идея, которая требует добавления канала для каждого символа максимальной длины префикса:

(?<=(\w\w:)|(\w:)) (.*\n?)\1?\2?

И замените на \t\3. См. test в regex101. Захват внутри lookbehind важен для того, чтобы не потреблять/не пропускать матч. Такая же переменная шаблона например,.NET: (?<=(\w+:)) (.*\n?)\1?

  • (?<=(\w\w:)|(\w:)) сначала две группы захвата внутри lookbehind для захвата префикса: два или один символ слова, за которым следует двоеточие. \w является shorthand для [A-Za-z0-9_]

  • (.*\n?) третья группа захвата для вещей между префиксами. Необязательная новая строка для получения последнего соответствия.

  • \1?\2? будет необязательно заменять тот же префикс, если в следующей строке. Можно установить только один из них: \1 xor \2. Также пространство после двоеточия всегда будет соответствовать - независимо от префикса.


Сводка: пробел после каждого префикса преобразуется в табуляцию. Префикс следующей строки, только если соответствует текущему.
& ЕПРС; & ЕПРС; & ЕПРС; & ЕПРС; Чтобы сопоставить и заменить несколько пробелов и вкладок: (? <= (\ w\w:) | (\ w:)) [\ t] + (. *\N?)\1?\2?

Ответ 2

Проблема с заменой - неопределенное количество совпадений. Когда вы ограничиваете это число, например. до 12, вы можете использовать регулярное выражение следующим образом:

^([^:]+): ([^\n]+[\n]*)(\1: ([^\n]+[\n]*))?(\1: ([^\n]+[\n]*))?(\1: ([^\n]+[\n]*))?(\1: ([^\n]+[\n]*))?(\1: ([^\n]+[\n]*))?(\1: ([^\n]+[\n]*))?(\1: ([^\n]+[\n]*))?(\1: ([^\n]+[\n]*))?(\1: ([^\n]+[\n]*))?(\1: ([^\n]+[\n]*))?(\1: ([^\n]+[\n]*))?(\1: ([^\n]+[\n]*))?

с этой заменой:

\n\1:\t\2\t\4\t\6\t\8\t\10\t\12\t\14\t\16\t\18\t\20\t\22\t\24

Объяснение: оно содержит в основном только два подрегрема

  • ^([^:]+): ([^\n]+[\n]*)= совпадает с первой строкой группы

  • (\1: ([^\n]+[\n]*))?= необязательные совпадения в последовательных строках, принадлежащих к одной и той же группе. Вы должны скопировать это регулярное выражение так часто, как нужно, чтобы соответствовать всем строкам (т.е. В этом случае 12x). Сопоставление ? (= ​​необязательно) не даст вам ошибки, если для всех подстановок недостаточно совпадений.

  • \n в начале подстановки требуется для проблемы с форматированием

  • результат будет содержать несколько пустых строк, но я уверен, вы можете решить это...; -)

DEMO 1

Однако, поскольку я не поклонник избыточных регулярных выражений, и для случая, когда у вас больше потенциальных совпадений, я бы предпочел такое решение:

DEMO 2

Ответ 3

Ниже вы найдете однострочный файл awk

awk -F: 'NR==1 {print $0} NR != 1 {if ($1 != prev) print $0; else {for (i=0; i<=length($1); ++i) printf " "; print $2;}} {prev=$1}' < input_file.txt

(поместите исходный текст в файл input_file.txt)

Я считаю, что можно написать более приятный код, но пришло время ложиться спать)

Ответ 4

Я попробовал ваш образец в Bare Bones Software Inc. TextWrangler и я придумали двухпроходное решение, которое ограничено n последовательными строками, и оно использует вкладку вместо того, чтобы волшебным образом соответствовать длине префикса. Также обратите внимание, что последняя строка файла должна быть пустой строкой (добавьте новую строку после , 6 в вашем примере)

В наших целях я показываю вам, где n = 4:

Find: ^([[:alnum:]]+\:)(.+\r)(?:\1(.+\r))?\1(.+)\r
Replace: \1\2\t\3\t\4\t\5\r

Вы можете добавить его к любому n, дублируя (?:\1(.+\r))? в Find и добавляя \t\n до \r в Replace, где * n * - это приращение после последнего числа, которое было до этого \r.

Заменяя все это, вы можете следить за ним:

Find: ^\t+
Replace: \t

В основном получить желаемый результат.

Ответ 5

Итак, поскольку вы хотели бы заменить все остальные экземпляры, кроме первого, я бы предположил, что вам нужно регулярное выражение для соответствия всем, кроме первого, чтобы вы могли их заменить. Регулярное выражение, как вы знаете, не может модифицировать или изменять исходную строку, возвращает только определенное соответствие, которое само может быть использовано для указания частей строки для модификации.

Лучшее регулярное выражение, которое я мог бы найти, - /(\b[a-zA-Z0-9]+: )[^\n]+(?:\n|$)(?!\1)/g.

Это будет захватывать каждый уникальный экземпляр xx: и соответствовать последним его экземплярам. Только проблема заключается в том, что он по-прежнему будет соответствовать последнему экземпляру, даже если это единственный экземпляр.

Мое заключение состоит в том, что я не верю, что вы можете делать все это с помощью регулярного выражения. Я могу ошибаться, если кто-то может найти отладчик онлайн-реджикса, который поддерживает lookbehind AND backreferencing, дайте мне знать и Я посмотрю, могу ли я написать выражение для работы. Я не мог лично найти отлаживатели регулярных выражений, которые принимают обратную связь и поиск. В моем примере я использую lookahead вместо этого, поэтому он проверяет, есть ли какие-либо его варианты вперед, если так игнорировать текущее соответствие (поэтому он выбирает только последний экземпляр).

Если вы действительно хотите найти способ автоматизировать это, чтобы заставить его работать, используйте /(\b[a-zA-Z0-9]+: )/g для соответствия каждому экземпляру xx:, сохраните их все в массиве и, если есть дубликат, запустите исходное регулярное выражение этот конкретный, чтобы продолжить обрезку, пока не будет больше дубликатов. Снова вы сможете использовать его для хранения всех уникальных экземпляров и использовать это как-то.

Надеюсь, что это поможет или прояснит вашу проблему, извините, если это не так.

Ответ 6

У меня нет Textwrangler для тестирования, но я тестирую это в другом инструменте Regex Tool, он работает хорошо, попробуйте:

(?<=(?:(?:.+\n)|^)(\w+?:).+\n)\1(?=\s)