В чем смысл \ r и \ n, означающих разные вещи в команде s?

13

Мы все знаем, что при поиске \nэто перевод строки и \rвозврат каретки ( ^M), но при замене \rэто перевод строки, тогда \nкак null byte ( ^@).

Каково происхождение этой асимметрии? Учитывая, что это поведение ... по меньшей мере своеобразно (и весьма контрпродуктивно, когда вы ошибаетесь в первый раз), я ожидаю, что есть какая-то странная историческая причина.

(кстати, есть ли способ «исправить» это поведение и получить что-то более интуитивное?)

Matteo Italia
источник

Ответы:

10

На самом базовом уровне уже есть асимметрия между частями поиска и замены, :substituteпотому что первое - это регулярное выражение, а второе - текст, с конкретными дополнительными escape-последовательностями . Это просто подчеркивается вашей интуицией о том, что \nзначит.

Например, учтите, что \nв поиске не соответствует литералу \n. Это соответствует концу строки (EOL) последовательности байтов, которые могут быть \r, \r\nили просто в \nзависимости от 'fileformat'буфера.

Что касается того, почему \rиспользуется для обозначения «вставить EOL», за этим стоит некоторая история . У Ви не было возможности обработать NUL-байт в файле. Vim улучшил это, заменив байты NUL внутренним байтом NL (поскольку строки C разделены NUL).

Эта деталь реализации просочилась в поведение, :substituteпоскольку \nпри замене просто вставляется во внутреннее представление этой строки, которое используется для указания байта NUL. \rвставляет EOL, разбивая внутреннюю строку на две части. На самом деле Vim не хранит байты EOL в памяти, а де-сериализует их при чтении / записи в буфер.

Это нельзя изменить сейчас, не нарушая множество скриптов и мышечной памяти многих пользователей. К счастью, это задокументировано в :help sub-replace-special.

jamessan
источник
6

NULБайт представляет собой строку в C - терминатор, и по этой причине Вим использует эту конвенцию, описанной в руководстве по :h NL-used-for-Nul:

Символы <Nul> в файле сохраняются в памяти как <NL>. На дисплее они отображаются как «^ @». Перевод сделан при чтении и записи файлов. Чтобы сопоставить <Nul> с шаблоном поиска, вы можете просто ввести CTRL- @ или "CTRL-V 000". Это, вероятно, именно то, что вы ожидаете. Внутренне символ заменяется на <NL> в шаблоне поиска. Что необычно, так это то, что при вводе CTRL-V CTRL-J также вставляет <NL>, таким образом, также ищет <Nul> в файле. {Vi вообще не может обрабатывать символы <Nul> в файле}

Это соглашение распространяется на :s/.../.../команду, но не на substitute()функцию. \rи \nв строках замены в substitute()вызовах сохраняют свое первоначальное значение.

Я не думаю, что есть более глубокие причины для любого поведения. Vim просто эволюционировал органически от оригинала vi. Для этого никогда не было никакого большого плана, функции были просто сложены друг на друга, с относительно небольшими усилиями, чтобы сохранить их организованными.

Сато Кацура
источник
0

Другие клоны Vi не поддерживают \rили \n(как настоящая обратная косая черта и буква) в подстановке, но поведение real ^M( CTRL-V Enter), означающее разделение строки на две строки, является стандартным поведением :

Ввод <carriage-return> в repl (который требует экранирования <backslash> в режиме ex и экранирования <control> -V в режиме open или vi ) должен разделить строку в этой точке, создав новую строку в буфере редактирования. , <Возврат каретки> отбрасывается.

В архиве истории Unix первая версия BSD ex / vi, в которой он появляется, - это 4.1cBSD ( @(#)ex_re.c 7.2 10/16/81и отсутствует в 4BSD ( @(#)ex_re.c 6.2 10/23/80) [4.1a и 4.1b отсутствуют в архиве].

Соответствующий код:

/* ^V <return> from vi to split lines */
if (c == '\r')
    c = '\n';

Это также упоминается в файле новостей :

Теперь можно разбить строки с помощью команд замены из vi, используя ^ V <return> в правой части. Это позаботится о последней веской причине для использования командного режима ex.

Ранее поддерживаемое поведение в бывшем командном режиме было для обратной косой черты (то есть обратной косой черты, за которой следует настоящая новая строка) для вставки новой строки.

Random832
источник
0

Происхождение асимметрии восходит к истории компьютеров.

Укороченная версия:

<CR> & <LF>  (Carriage-Return and Linefeed) 
== 
\r & \n

Длинная версия:
первые экраны были в основном цифровыми версиями телетайпа (TTY) и использовали управляющие коды для генерации поведения, аналогичного принтерам. Возврат каретки переместил курсор (или печатающую головку) в начальный столбец. Перевод строки переместился на следующий ряд (на экране) и подал бумагу вперед на одну строку.

Для принтеров вы должны были сделать сопряжение, <CR><LF>иначе ваш вывод не будет выглядеть правильно. На ранних экранах проблема все еще сохранялась.

DOS (и sorta-Windows после) следуют старому стандарту и сохраняют текст с помощью <CRLF>.

* Текст NIX (как большинство пользователей vi знакомы) использует только <LF>для эффективности.

Для тестирования в Windows используйте Word / Wordpad и сохраните несколько строк текста «как тип: Текст - формат MS-DOS». Затем откройте тот же файл в блокноте. Это должно выглядеть нормально. Затем сохраните тот же файл в Word / Wordpad «как type: Text». Блокнот будет игнорировать все новые строки и запускать строки вместе. [Формат текста блокнота по умолчанию соответствует \r\nкомбинации, в то время как Word / Wordpad по умолчанию равен \n.]

\ r является кодовым эквивалентом <CR>

\ n - это эквивалент кода <LF>

И по моему (очень ограниченному) опыту работы с vi, он попытался бы «исправить» <CRLF>комбинацию из моего текстового редактора DOS. В итоге vi удалил один символ и заменил на <NUL>. Большая часть причины, по которой я перестал использовать vi.

Робин
источник
2
Хотя вся ваша информация интересна, она только говорит, почему \rесть <CR>и \nесть <LF>. Это не относится к актуальному вопросу о том, почему \n\rведут себя по- разному в разных контекстах.
Tumbler41
Спасибо! :-) Я изменил это, когда вы ответили. (Добавлен последний абзац.)
Робин