Почему "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
.