Почему "hallo\nworld" соответствует как \n, так и \\n в R?

Почему grep обрабатывает \n и \\n таким же образом?

Например, оба соответствуют hallo\nworld.

grep("hallo\nworld", pattern="\n")
[1] 1
grep("hallo\nworld", pattern="\\n")
[1] 1

Я вижу, что hallo\nworld анализируется на

hallo  
world

то есть hallo в одной строке и world на одной строке.

Итак, в grep("hallo\nworld", pattern="\n"), есть ли pattern="\n" новая строка или \n буквально?

Также обратите внимание, что это происходит с другими; \a \f \n \t \r и \\a \\f \\n \\t \\r обрабатываются одинаково. Но \d \w \s не может быть использован! Почему бы и нет?

Я выбрал разные строки для тестирования, и я нашел секрет в понятии регулярного выражения.

Есть два понятия побега, один - побег в строке, его просто понять; другой - escape в строке регулярного выражения шаблона. В R шаблон, такой как grep(x, pattern=" some string here "), \\n= \n= символ новой строки. Но в общей строке, \\n!= \n, первая буквально \n, последняя является символом новой строки. Мы можем доказать это следующим образом:

cat("\n")

cat("\\n")
\n> 

Как это доказать? Я попытаюсь использовать другие символы, а не только \n, чтобы увидеть, совпадают ли они одинаково.

special1 <- c( "\a", "\f", "\n", "\t", "\r")
special2 <- c("\\a","\\f","\\n","\\t","\\r")
target <- paste("hallo", special1, "world", sep="")
for (i in 1:5){
    cat("i=", i, "\n")
    if( grep(target[i], pattern=special1[i]) == 1)
        print(paste(target[i], "match", special1[i], "succeed"))
    if( grep(target[i], pattern=special2[i]) == 1)
        print(paste(target[i], "match", special2[i], "succeed"))
}

выход:

i= 1   
[1] "hallo\aworld match \a succeed"  
[1] "hallo\aworld match `\\a` succeed"  
i= 2   
[1] "hallo\fworld match \f succeed"  
[1] "hallo\fworld match `\\f` succeed"  
i= 3   
[1] "hallo\nworld match \n succeed"  
[1] "hallo\nworld match `\\n` succeed"  
i= 4   
[1] "hallo\tworld match \t succeed"  
[1] "hallo\tworld match `\\t` succeed"  
i= 5   
[1] "hallo\rworld match \r succeed"  
[1] "hallo\rworld match `\\r` succeed" 

Обратите внимание, что \a \f \n \t \r и \\a \\f \\n \\t \\r все обрабатывались одинаково в строке регулярного выражения R!

Не только это, вы не можете написать \d \w \s в R регулярном шаблоне выражения!
Вы можете написать любой из них:

pattern="\a" "pattern=\f" "pattern=\n" "pattern=\t" "pattern=\r"

Но вы не можете написать ни одного из них!

pattern="\d" "pattern="\w" "pattern=\s"  in grep.

Я думаю, что это тоже ошибка, так как \d \w \s обрабатываются неравномерно с \a \f \n \t \r.

Ответы

Ответ 1

Причина, по которой \n, \\n и \\\n совпадают, - это двойная оценка шаблона поиска. Я заметил это, выполнив несколько примеров:

grep("hello\nworld", pattern="\n")
[1] 1
grep("hello\nworld", pattern="\\n")
[1] 1
> grep("hello\nworld", pattern="\\\n")
[1] 1
> grep("hello\nworld", pattern="\\\\n")
integer(0)
> grep("hello\\nworld", pattern="\\\\n")
[1] 1

Имейте в виду правила оценки escape-последовательностей обратной косой черты:

  • \\ заменяется на \
  • \n заменяется символом NEWLINE
  • \ + NEWLINE заменяется символом NEWLINE
  • (подробнее см. документы в ?regex).

С учетом этого, если вы дважды оцениваете шаблон, вы получаете:

  • \n = > NEWLINE = > NEWLINE
  • \\n = > \n = > NEWLINE
  • \\\n = > \ + NEWLINE = > NEWLINE
  • \\\\n = > \\n = > \n
  • \\\\\n = > \\ + NEWLINE = > \ + NEWLINE
  • \\\\\\n = > \\\n = > \ + NEWLINE
  • \\\\\\\n = > \\\ + NEWLINE = > \ + NEWLINE
  • \\\\\\\\n = > \\\\n = > \\n

И так далее. В примерах 1-3 все оцениваются до одного NEWLINE, поэтому эти шаблоны будут соответствовать. (В то же время строка, которую вы пытаетесь сопоставить с шаблоном, оценивается только один раз.)

Обсуждение в списке рассылки R, отправленное @Aaron, объясняет двойная оценка:

Существует два уровня [оценки], поскольку обратная косая черта является символами escape-символов как для R строк и регулярных выражений.

Обратите внимание, что другие языки не оценивают такие шаблоны. Возьмем, например, Python:

import re
>>> re.search(r'\n', 'hello\nworld') is not None
True
>>> re.search(r'\\n', 'hello\nworld') is not None
False

Или Perl:

$ perl -e 'print "hello\nworld" =~ /\n/ || 0, "\n"'
1
$ perl -e 'print "hello\nworld" =~ /\\n/ || 0, "\n"'
0

И мы могли бы продолжить. Таким образом, двойная оценка в R кажется необычной. Почему это реализовано таким образом? Я думаю, что окончательный ответ лежит на R-devel.

БЛАГОДАРНОСТЬ

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

Ответ 2

Примечание, что обратная косая черта является специальной, вам нужно избежать обратного слэш с обратным слэшем.

\\n означает "Я действительно хочу совместить символ новой строки, а не буквальный \n"

grep("hallo\nworld", pattern = "\\n")
[1] 1

grep("hallo\\nworld", pattern = "\\\\n")
[1] 1

Ответ 3

После ответа hwnd взгляните на следующее:

cat("x\ny")
## x
## y
cat("x\\ny")
## x\ny
grep("hallo\nworld", pattern="[\n]")
## [1] 1
grep("hallo\nworld", pattern="[\\n]")
## integer(0)

Итак: "\n" - это буквальная новая строка, "\\n" - обратная косая черта + n, которая интерпретируется grep как новая строка. Поэтому в моем первом примере найдено совпадение (поиск любого символа в наборе { newline }), а в моем втором примере совпадение не найдено (поиск любого символа в наборе { \ n }).

Это не ошибка, это вполне ожидаемое поведение. На этой ноте, чтобы быть действительно и абсолютно уверенным, почему бы вам post в R-help или R-devel?

Ответ 4

Это действительно связано с "двойным побегом", упомянутым lebatsnok. Как писал Питер Далгаард в R-help, "обратные косые черты являются символами escape-символов как для строк R, так и для регулярных выражений". См. https://stat.ethz.ch/pipermail/r-help/2003-August/037524.html. Также см. Примечание о удвоении обратных косых черт в ?regex, хотя для меня это не так ясно, как комментарий Dalgaard.

Итак, \n становится символом новой строки в первом проходе и остается таким же во втором.

\\n переходит в \n в первый проход (\\\), а затем становится второй строкой во второй.

\\\n становится \, за которым следует первая строка в первом проходе, которая, по-видимому, становится только новой строкой во втором проходе, поскольку она также соответствует.

Кроме того, о проблеме с a, f, n, t и r, имеющей обратную косую черту, но d, w и s нет, обратите внимание, что это специальные метасимволы со специальным значением, как указано в ?regex:

Текущая реализация интерпретирует      '\ a' как 'BEL', '\ e' как 'ESC', '\ f' как 'FF', '\n' как 'LF', '\ r' as      'CR' и '\ t' как 'TAB'.

Ответ 5

Поскольку строка hallo\nworld содержит литеральный текст \n, а также символ line feed, когда он разбирается.

Если ваша строка была фактически:

hallo
world

Тогда он будет соответствовать только \n, а не \\n.