V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
推荐关注
Meteor
JSLint - a JavaScript code quality tool
jsFiddle
D3.js
WebStorm
推荐书目
JavaScript 权威指南第 5 版
Closure: The Definitive Guide
supermoonie
V2EX  ›  JavaScript

求个 js 正则,脑子最近进水,暂时想不出来

  •  
  •   supermoonie · 2020-07-04 23:09:04 +08:00 · 3716 次点击
    这是一个创建于 1605 天前的主题,其中的信息可能已经有所发展或是发生改变。

    输入如下: Content-Disposition: form-data; name="test.png"; filename="test.jpg" 或 Content-Disposition: form-data; name="foo"

    输出: ['test.png', 'test.jpg'] 或 ['foo']

    哪位大佬指导下?[献花][献花][献花]

    45 条回复    2020-07-06 23:23:12 +08:00
    creanme
        1
    creanme  
       2020-07-04 23:18:38 +08:00 via Android
    手机回复的,也没测试,就当瞎写的。
    name=(".*\.png");filename=(".*\.jpg")

    用$1 与$2 输出
    mingl0280
        2
    mingl0280  
       2020-07-04 23:20:54 +08:00
    (\")(\S+)(\")取第二组。
    mingl0280
        3
    mingl0280  
       2020-07-04 23:21:31 +08:00
    https://regex101.com/
    这个网站可以直接测试正则,挺好用的。
    aheadin
        4
    aheadin  
       2020-07-04 23:25:27 +08:00   ❤️ 1
    function(str) {
    return [...str.matchAll(/name="([\w+.]+)"/g)].map(item => item[1])
    }
    supermoonie
        5
    supermoonie  
    OP
       2020-07-04 23:28:53 +08:00
    @mingl0280 /(?:")(\S+)(?:")/.exec('Content-Disposition: form-data; name="test.png"; filename="test.jpg"') 稍微改了下,返回的是 [""test.png"", "test.jpg"],第一个多了双引号。。。
    supermoonie
        6
    supermoonie  
    OP
       2020-07-04 23:33:06 +08:00
    @aheadin niubility,大佬能一个正则匹配出来吗?
    supermoonie
        7
    supermoonie  
    OP
       2020-07-04 23:34:02 +08:00
    @creanme 第二种情况满足不了😂
    ochatokori
        8
    ochatokori  
       2020-07-04 23:45:02 +08:00
    js 的正则最简单了,挖空填入(.*?)就好了
    ```javascript
    text.match(/name="(.*?)"(.*?)filename="(.*?)"/)
    // 取数组的 1 和 3
    如果想第二种情况节 /name="(.*?)"/
    ```
    autoxbc
        9
    autoxbc  
       2020-07-04 23:45:50 +08:00   ❤️ 3
    正则就是圣杯,不管什么问题搞成正则就对了 <-- 害人不浅
    sedgwickz
        10
    sedgwickz  
       2020-07-04 23:47:06 +08:00   ❤️ 1
    'Content-Disposition: form-data; name="test.png"; filename="test.jpg" 或 Content-Disposition: form-data; name="foo"'.match(/(?<=(name\="|filename\=")).*?(?=")/g)
    supermoonie
        11
    supermoonie  
    OP
       2020-07-04 23:47:53 +08:00
    @ochatokori 分开写,我也会😂,我在尝试一个正则怎么给取出来,貌似有点难。。。
    lidlesseye11
        12
    lidlesseye11  
       2020-07-04 23:50:35 +08:00
    。。。你这个需求不就是取引号里的东西吗?
    supermoonie
        13
    supermoonie  
    OP
       2020-07-04 23:50:50 +08:00
    @sedgwickz niubility,大佬一出手就知有没有
    supermoonie
        14
    supermoonie  
    OP
       2020-07-04 23:53:44 +08:00
    @lidlesseye11 是的呢,目前只看到一位大佬写出来了,小弟反正是写不出来😂
    lidlesseye11
        15
    lidlesseye11  
       2020-07-05 00:11:43 +08:00
    @supermoonie
    那 split 比较简单粗暴吧。。
    function getName(str) {
    var arr = str.split("\"");
    var rtnArr = new Array();
    for(var i = 0;i < arr.length; i++) {
    if (i%2!=0) rtnArr.push(arr[i]);
    }
    return rtnArr;
    }
    supermoonie
        16
    supermoonie  
    OP
       2020-07-05 00:23:12 +08:00
    @lidlesseye11 你不觉得 /(?<=(name="|filename=")).*?(?=")/g 这个正则很完美吗?( emmm... 主要是项目是自己的,所以自己能看懂就行了,虽然我知道 split 更直接更易读,但是一旦有了想用正则去做,就会不死不休😂)
    mingl0280
        17
    mingl0280  
       2020-07-05 00:27:07 +08:00
    @supermoonie 正则匹配下来应该是个 iterator 啊,你在想啥
    ```
    var str = 'Content-Disposition: form-data; name="test.png"; filename="test.jpg"'
    var matches = str.matchAll(/(")(\S+)(")/g)
    for(const match of matches)
    {
    console.log(`${match[2]}`)
    }
    ```
    xiaoming1992
        18
    xiaoming1992  
       2020-07-05 00:29:31 +08:00
    const str = ""

    const reg = /(?<=name=\")(.+?)(?=\")/g

    let exec = reg.exec(str)

    const resultArray = []

    while (exec) {

    __resultArray.push(exec[0])

    __reg.exec(str)

    }
    supermoonie
        19
    supermoonie  
    OP
       2020-07-05 00:33:40 +08:00
    @mingl0280 想的是一行代码搞定,有就返回['test.png', 'test.jpg'] 或者 ['foo'] ,没有就返回 null,/(?<=(name="|filename=")).*?(?="\s+)/g 这个正则不是更香吗?
    supermoonie
        20
    supermoonie  
    OP
       2020-07-05 00:34:09 +08:00
    @xiaoming1992 谢了,前面有位大佬提供了 /(?<=(name="|filename=")).*?(?="\s+)/g
    alan0liang
        21
    alan0liang  
       2020-07-05 09:14:07 +08:00 via Android
    略微有点偏题: https://alf.nu/RegexGolf
    justgodlike1993
        22
    justgodlike1993  
       2020-07-05 10:25:42 +08:00
    "[^"]*?"
    no1xsyzy
        23
    no1xsyzy  
       2020-07-05 14:47:57 +08:00
    @supermoonie #16 完美?
    /(?<=name=").*?(?=")/g
    ,请?同后缀 lookbehind 竟然不合并…… 而且不需要匹配的部分不应该 (...) 而是 (?:...)
    no1xsyzy
        24
    no1xsyzy  
       2020-07-05 15:07:19 +08:00
    对了,/(?<=(name="|filename=")).*?(?="\s+)/g 有若干问题
    1. lookbehind 在 Firefox < 78 不可用(参考: https://caniuse.com/#feat=js-regexp-lookbehind ),这个主题让我发现我该更新了。更新到 78 就可用了;另外 #3 似乎会根据浏览器版本来改变 JavaScript 下的效果(或者是因为就是用的浏览器实现)。
    2. (?="\s+) 严格要求 lookahead 是一个引号和至少一个空白符。而你的第一个输入中,test.png 后面是 "; 一个引号一个分号,也就不会被获取,应该改成 (?=") (我发现你过了十分钟后的回复多了 \s+ )
    3. 不要 capturing group 不需要的部分,应该用 non-capturing group 。
    4. 如果是 Perl 下面,lookbehind 要求宽度一致。name="|filename=" 宽度不一致。
    no1xsyzy
        25
    no1xsyzy  
       2020-07-05 15:10:07 +08:00
    另外,根据上述 caniuse
    你这个正则只有在 71% 左右的浏览器上可运行。
    Safari 全灭。
    supermoonie
        26
    supermoonie  
    OP
       2020-07-05 20:25:47 +08:00
    @no1xsyzy 后来多的 \s+ 是我自己加上去做测试的,忘记去掉了。前端的兼容性问题真是让人脑壳疼,没想到一个正则,各家浏览器支持竟然不一样,感谢大佬科普!由于是项目初期,先保证在 Chrome 能正常使用,所以兼容性目前不是考虑的重点,后期会不停迭代进行优化。(想尽早出一个版本,一个人的精力有限😂)
    jiejiss
        27
    jiejiss  
       2020-07-05 20:43:20 +08:00
    1. 请写 string parser,你这个需求用正则带来的心智负担更大
    2. 正则不是银弹,不是所有情况下都要用正则
    3. 想练手可以去 https://www.hackerrank.com/domains/regex,想进阶可以去 https://alf.nu/RegexGolf
    4. https://stackoverflow.com/questions/1732348/regex-match-open-tags-except-xhtml-self-contained-tags
    supermoonie
        28
    supermoonie  
    OP
       2020-07-05 20:56:48 +08:00
    @no1xsyzy 现在我能想到的最简单的正则就是 /"([^;]*)"/g 这个了,匹配到的结果再把双引号给 replace 掉。。。大佬有好的思路吗?
    supermoonie
        29
    supermoonie  
    OP
       2020-07-05 21:00:01 +08:00
    @jiejiss 个人项目,一开始只是想用正则试下,发现这个正则不是太好写,就来问问各位大佬,扩展下思路,学习了[抱拳]
    supermoonie
        30
    supermoonie  
    OP
       2020-07-05 21:00:37 +08:00
    @justgodlike1993 用的这个 /"([^;]*)"/g 正则,最后再把双引号 replace 掉
    no1xsyzy
        31
    no1xsyzy  
       2020-07-05 22:41:15 +08:00
    发现之前写完了没发(
    @supermoonie #28 /"([^"]*)"/g 然后拿 group 1 。反正文件名带引号极少(因为 Windows 下不支持),有引号也应该转义的,而且因为是 web,转义是 % 的做法,所以排除引号很稳定。
    @jiejiss #27 XML 是因为可递归所以不能用正则。
    jiejiss
        32
    jiejiss  
       2020-07-05 23:16:02 +08:00
    @no1xsyzy #31 可递归也不一定不能用正则的,现在很多语言的正则引擎支持很多 irregular 的 feature 比如 lookbehind 和引用,我甚至可以用正则匹配正整数域内全部 3/5/7 的倍数。甚至 perl 的正则都是图灵完备的了

    关键其实在于正则有合适的使用场景,我希望的是楼主看完这篇回答能认识到正则不是万能的
    supermoonie
        33
    supermoonie  
    OP
       2020-07-05 23:20:25 +08:00
    @no1xsyzy
    https://assets-1253328229.cos.ap-shanghai.myqcloud.com/assets/20200705231821.png
    这点倒是提醒我了,文件名中带双引号,浏览器可能会转译,不过 Apache HttpClient 发出的请求却是 \" ,还是要处理下
    supermoonie
        34
    supermoonie  
    OP
       2020-07-05 23:26:27 +08:00
    @jiejiss 受教了,楼猪主要从事 Java 开发,所以对前端这块不是特别熟悉,现在已经能不用正则就不用正则了
    no1xsyzy
        35
    no1xsyzy  
       2020-07-05 23:27:37 +08:00
    @jiejiss #32 也是,重点在于不必拿正则做一切的观念,而不仅仅是具体某个问题是否适合。
    不过,在当前主题下,我觉得没必要为了一行去搞个 parser,反复 split,相比之下单纯针对引号会更方便一点。
    如果是大的物件的一部分,已经引入了 HTTP 相关内容还是 parser 比较好。
    no1xsyzy
        36
    no1xsyzy  
       2020-07-05 23:37:52 +08:00
    @supermoonie #33 我的错,拍脑袋了
    https://www.w3.org/Protocols/rfc2616/rfc2616-sec19.html#sec19.5.1
    https://www.w3.org/Protocols/rfc2616/rfc2616-sec2.html#sec2.2
    这里是一个 quoted-string,转义该用的就是 \"
    /"((?:\.|[^"\\])*)"/g
    supermoonie
        37
    supermoonie  
    OP
       2020-07-05 23:39:08 +08:00
    @no1xsyzy 其实需求是解析 HTTP 请求中 multipart 并展示,要把每一个 formdata 中的内容都解析出来,如果不用正则的话,就要先 split('\n') 分行,然后再每一行判断是否为 Content-Disposition,然后再解析,如果用正则的话,就可以根据 Content-Disposition 关键字一步到位,最终效果如图:
    https://assets-1253328229.cos.ap-shanghai.myqcloud.com/assets/20200705233841.png
    supermoonie
        38
    supermoonie  
    OP
       2020-07-05 23:45:47 +08:00
    @no1xsyzy 大佬 666 啊,正则随手就来,协议链接也很有说服力,佩服佩服
    supermoonie
        39
    supermoonie  
    OP
       2020-07-06 00:21:04 +08:00
    @no1xsyzy 最终方案:

    let _sub = function(str) {
    return [...str.matchAll(/name="([^\s]+)"/g)].map(item => item[1]);
    };
    MrUser
        40
    MrUser  
       2020-07-06 09:17:52 +08:00
    (?<==").*?(?=")/g

    JS 测试代码:

    'Content-Disposition: form-data; name="test.png"; filename="test.jpg" 或 Content-Disposition: form-data; name="foo"'.match(/(?<==").*?(?=")/g)

    // 输出: ["test.png", "test.jpg", "foo"]

    规则说明:匹配双引号里边的内容,开头双引号要求是“="”
    MrUser
        41
    MrUser  
       2020-07-06 09:28:11 +08:00
    正则的前置断言各浏览器支持性确实不好,收回以上回复
    supermoonie
        42
    supermoonie  
    OP
       2020-07-06 09:41:59 +08:00
    @MrUser Safari 全军覆没。。。
    creanme
        43
    creanme  
       2020-07-06 21:19:27 +08:00
    我朋友写了个 /(?:(?:file)?name=)"([^"]+")/g,但是没法正常输出 groups,不知道为啥。string 的 match 在有 global 标志的情况下,不会输出 groups,只会输出匹配项。

    regex 的 exec 则 不管是 new Regexp('(?:(?:file)?name=)"([^"]+")','g')还是 new Regexp(/(?:(?:file)?name=)"([^"]+")/g)都没法匹配全局。
    creanme
        44
    creanme  
       2020-07-06 21:35:36 +08:00
    想通了,exec 有个 lastindex 标志位,所以不会一次输出全局的匹配。
    supermoonie
        45
    supermoonie  
    OP
       2020-07-06 23:23:12 +08:00
    @creanme 我已经抛弃 js 的正则了😂
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   实用小工具   ·   3535 人在线   最高记录 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 34ms · UTC 00:47 · PVG 08:47 · LAX 16:47 · JFK 19:47
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.