V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
alex8
V2EX  ›  PHP

正则/^[a-z]+$/只有 PHP 匹配换行符,这算是 PHP 的 bug 么

  •  5
     
  •   alex8 · 2019-06-21 09:21:52 +08:00 · 4676 次点击
    这是一个创建于 2017 天前的主题,其中的信息可能已经有所发展或是发生改变。
    # php 7.2.16
    var_dump(preg_match("/^[a-z]+$/", "abc\n"));
    #int(1)
    
     # javascript
    var reg = /^[a-z]+$/;
    console.log(reg.test("abc\n"));
    # 打印的是 false
    
    # java(jdk11)
    System.out.println(Pattern.matches("^[a-z]+$", "abc\n"));
    # 打印的是 false
    
    第 1 条附言  ·  2019-06-21 09:58:09 +08:00
    单引号(')下的\n 不会被转义,请各位不要再讨论单引号下\n 的话题了,没有意义。
    第 2 条附言  ·  2019-06-21 10:23:53 +08:00
    找到答案了,PHP 中正则特性,各位 PHPer 以后写正则匹配单行时记得加上修饰符 D

    https://www.regular-expressions.info/anchors.html
    "Because Perl returns a string with a newline at the end when reading a line from a file, Perl's regex engine matches $ at the position before the line break at the end of the string even when multi-line mode is turned off. Perl also matches $ at the very end of the string, regardless of whether that character is a line break. So ^\d+$ matches 123 whether the subject string is 123 or 123\n."

    https://www.php.net/manual/en/reference.pcre.pattern.modifiers.php

    "D (PCRE_DOLLAR_ENDONLY)
    If this modifier is set, a dollar metacharacter in the pattern matches only at the end of the subject string. Without this modifier, a dollar also matches immediately before the final character if it is a newline (but not before any other newlines). This modifier is ignored if m modifier is set. There is no equivalent to this modifier in Perl."
    33 条回复    2019-06-21 14:11:15 +08:00
    yuwangG
        1
    yuwangG  
       2019-06-21 09:34:04 +08:00
    你对比的不是一个东西, 你把 var_dump(preg_match("/^[a-z]+$/", "abc\n"));
    #int(1)
    yuwangG
        2
    yuwangG  
       2019-06-21 09:35:24 +08:00
    换成 var_dump(preg_match("/^[a-z]+$/", 'abc\n')); 就会输出 #int(0)。 注意 PHP 双引号和单引号区别
    no1xsyzy
        3
    no1xsyzy  
       2019-06-21 09:35:54 +08:00   ❤️ 1
    没自己用过 PHP,但我合理猜测是 $ 匹配到了 \n 的前面
    no1xsyzy
        4
    no1xsyzy  
       2019-06-21 09:36:34 +08:00
    自己->仔细
    no1xsyzy
        5
    no1xsyzy  
       2019-06-21 09:37:54 +08:00
    @yuwangG 你在说什么鬼,1 分钟的搜索引擎告诉我单引号下 \n 不是换行符。
    back0893
        6
    back0893  
       2019-06-21 09:42:01 +08:00
    @no1xsyzy php 里面还真不是 使用单引号那么就是 \n 的字符串
    Youngxj
        7
    Youngxj  
       2019-06-21 09:42:51 +08:00
    var_dump(preg_match("/^[a-z]+$/", 'abc\n'));
    输出的确实为 int(0),应该是 php 的双引号会把\n 解析掉,单引号是文本,所以不会解析掉
    ben1024
        8
    ben1024  
       2019-06-21 09:46:18 +08:00
    单双引号
    $str = 'abc';
    var_dump(preg_match("/^[a-z]+$/", "{$str}\n"));
    int 1

    var_dump(preg_match("/^[a-z]+$/", 'abc\n'));
    int 0
    alex8
        9
    alex8  
    OP
       2019-06-21 09:47:33 +08:00
    @yuwangG
    请先了解 PHP 单引号和双引号的区别
    SakuraKuma
        10
    SakuraKuma  
       2019-06-21 09:51:25 +08:00
    我寻思着一楼有啥问题??
    你把 match 出来的东西看看不就知道了。。
    golden0125
        11
    golden0125  
       2019-06-21 09:51:39 +08:00
    @no1xsyzy 麻烦质疑前先自己测试一下
    no1xsyzy
        12
    no1xsyzy  
       2019-06-21 09:57:32 +08:00
    @back0893 https://tio.run/##K8go@P/fxr4go4CroCgzr0RBPTEpOSZP3RrKVbKyUoKzwVJKqFL2dv//AwA
    请?
    'abc\n' 是一个五字符的串,对应 list 是 '(#\a #\b #\c #\\ #\n) 或者说 '(97 98 99 92 110)
    "abc\n" 是一个四字符的串,对应 list 是 '(#\a #\b #\c #\newline) 或者说 '(97 98 99 10)
    no1xsyzy
        13
    no1xsyzy  
       2019-06-21 09:58:40 +08:00
    @golden0125 您测试了?见 #12。
    wu1990
        14
    wu1990  
       2019-06-21 09:59:10 +08:00
    /(*CRLF)^[a-z]+$/
    no1xsyzy
        15
    no1xsyzy  
       2019-06-21 09:59:54 +08:00
    ThirdFlame
        16
    ThirdFlame  
       2019-06-21 10:03:21 +08:00
    是的 php 的 preg_match 中$ 可以命中字符串结尾 也可以命中 chr(10) , CTF 比赛经常会出现这个。
    golden0125
        17
    golden0125  
       2019-06-21 10:04:32 +08:00
    @no1xsyzy PHP 初学者都知道的问题,双引号下\n 被转义了,单引号不会被转义,有这么难理解吗?
    no1xsyzy
        18
    no1xsyzy  
       2019-06-21 10:08:27 +08:00
    @wu1990 这个方法……是把换行符设置成 \r\n 才算吗?
    leo108
        19
    leo108  
       2019-06-21 10:08:32 +08:00
    @golden0125 建议看明白楼主的问题之后再来杠
    no1xsyzy
        20
    no1xsyzy  
       2019-06-21 10:09:43 +08:00
    @golden0125 我说的不是这个?
    #5
    > 单引号下 \n 不是换行符
    zsdroid
        21
    zsdroid  
       2019-06-21 10:12:16 +08:00   ❤️ 5
    /^[a-z]+$/D

    php 正则用的是 Perl 兼容正则表达式

    $ 结尾处默认忽略回车

    加 D 就变成不忽略回车
    golden0125
        22
    golden0125  
       2019-06-21 10:15:26 +08:00
    @no1xsyzy 你 @的 1 2 楼不也是说的单双引号的区别?

    "你在说什么鬼,1 分钟的搜索引擎告诉我单引号下 \n 不是换行符。"

    他说过单引号下 \n 是换行符?
    alex8
        23
    alex8  
    OP
       2019-06-21 10:15:37 +08:00   ❤️ 1
    @zsdroid
    是的,应该是 PHP 中独有的正则特性
    https://www.php.net/manual/en/reference.pcre.pattern.modifiers.php
    golden0125
        24
    golden0125  
       2019-06-21 10:16:00 +08:00
    @leo108 麻烦你看明白我的问题后再来杠
    no1xsyzy
        25
    no1xsyzy  
       2019-06-21 10:18:48 +08:00   ❤️ 1
    对了回到楼主的问题,看来不是 bug 是 feature ……
    PCRE 这套正则就是这么设计的,as spec。参考:<https://php.net/manual/zh/reference.pcre.pattern.modifiers.php>
    『默认情况下,PCRE 认为目标字符串是由单行字符组成的(然而实际上它可能会包含多行), "行首"元字符 (^) 仅匹配字符串的开始位置, 而"行末"元字符 ($) 仅匹配字符串末尾, 或者最后的换行符(除非设置了 D 修饰符)。』
    所以正确的做法是加修饰符 D,样例:
    https://tio.run/##K8go@P/fxr4go4CroCgzr0ShLLEoPqU0t0CjoCg1PT43sSQ5Q0NJPy46UbcqVltFX0lHQSkxKVlJU9OaOA0uUB0xeWA99nb//wMA
    no1xsyzy
        26
    no1xsyzy  
       2019-06-21 10:20:28 +08:00
    @golden0125 看楼主标题 “只有 PHP 匹配换行符”。
    而 #1#2 答非所问,要问个换行符相关的问题,结果回答是 “把换行符去掉就行了”。你觉得这是回答?
    yuwangG
        27
    yuwangG  
       2019-06-21 10:20:34 +08:00
    @zsdroid 是的,学习了~ thanks
    golden0125
        28
    golden0125  
       2019-06-21 10:26:01 +08:00
    @no1xsyzy 我没看到他回答过 “把换行符去掉就行了” 他回答的很清楚,注意单双引号的区别
    zarte
        29
    zarte  
       2019-06-21 10:30:36 +08:00
    @zsdroid
    终于等到一个解决的回复了。
    yuwangG
        30
    yuwangG  
       2019-06-21 10:35:12 +08:00
    @no1xsyzy 确实, 理解题目出现了偏差,尴尬~
    no1xsyzy
        31
    no1xsyzy  
       2019-06-21 10:36:31 +08:00
    @golden0125 把 "\n" 换成 '\n' 不是把换行符去掉?换成了 #\\ 和 #\n。
    你说说 'abc\n' 里哪来的换行符?换成单引号哪里符合楼主的提问?
    况且那个 "abc\n" 实际使用基本不会是字面量而是字符串变量( Pattern 是变量而待匹配字符串是常量的情况比较少,而且这种情况一般也不会产生换行结尾的),这种情况下你怎么改单引号?
    没理了还强行杠?
    dyllen
        32
    dyllen  
       2019-06-21 13:53:00 +08:00
    @sunweiqiang8 不是独有吧,php 的文档里面明确说明的 php 实现的正则是和 perl 一样的,那估计 perl 里面也是这样的。
    alex8
        33
    alex8  
    OP
       2019-06-21 14:11:15 +08:00
    @dyllen

    修饰符 D 的描述最后一行:
    There is no equivalent to this modifier in Perl
    在 Perl 中没有与此修饰符等效的东西
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   实用小工具   ·   2709 人在线   最高记录 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 25ms · UTC 14:39 · PVG 22:39 · LAX 06:39 · JFK 09:39
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.