正则表达式从查询字符串中删除一个参数

时间:2009-12-03 20:27:54

标签: regex query-string

我正在寻找一个正则表达式来从查询字符串中删除单个参数,如果可能的话,我希望在单个正则表达式中执行此操作。

我想要删除foo参数。现在我用这个:

/&?foo\=[^&]+/
只要foo不是查询字符串中的第一个参数,

就可以正常工作。如果是,那么我的新查询字符串以&符号开头。 (例如,“foo=123&bar=456”给出“&bar=456”的结果。)现在,我只是在正则表达式后检查查询字符串是否以&符号开头,如果是,则将其砍掉

示例边缘情况:

Input                    |  Expected Output
-------------------------+--------------------
foo=123                  |  (empty string)
foo=123&bar=456          |  bar=456
bar=456&foo=123          |  bar=456
abc=789&foo=123&bar=456  |  abc=789&bar=456

修改

好的,正如评论中指出的那样,有更多的边缘案例比我原先考虑的更多。我得到以下正则表达式与所有人一起工作:

/&foo(\=[^&]*)?(?=&|$)|^foo(\=[^&]*)?(&|$)/

这是从Mark Byers's answer修改的,这就是为什么我接受那个,但罗杰佩特的投入也帮了很多。

以下是我正在使用的全套测试用例,以及一个测试它们的Javascript代码段:

$(function() {
    var regex = /&foo(\=[^&]*)?(?=&|$)|^foo(\=[^&]*)?(&|$)/;
    
    var escapeHtml = function (str) {
        var map = {
          '&': '&',
          '<': '&lt;',
          '>': '&gt;',
          '"': '&quot;',
          "'": '&#039;'
        };
        
        return str.replace(/[&<>"']/g, function(m) { return map[m]; });
    };

    
    //test cases
    var tests = [
        'foo'     , 'foo&bar=456'     , 'bar=456&foo'     , 'abc=789&foo&bar=456'
       ,'foo='    , 'foo=&bar=456'    , 'bar=456&foo='    , 'abc=789&foo=&bar=456'
       ,'foo=123' , 'foo=123&bar=456' , 'bar=456&foo=123' , 'abc=789&foo=123&bar=456'
       ,'xfoo'    , 'xfoo&bar=456'    , 'bar=456&xfoo'    , 'abc=789&xfoo&bar=456'
       ,'xfoo='   , 'xfoo=&bar=456'   , 'bar=456&xfoo='   , 'abc=789&xfoo=&bar=456'
       ,'xfoo=123', 'xfoo=123&bar=456', 'bar=456&xfoo=123', 'abc=789&xfoo=123&bar=456'
       ,'foox'    , 'foox&bar=456'    , 'bar=456&foox'    , 'abc=789&foox&bar=456'
       ,'foox='   , 'foox=&bar=456'   , 'bar=456&foox='   , 'abc=789&foox=&bar=456'
       ,'foox=123', 'foox=123&bar=456', 'bar=456&foox=123', 'abc=789&foox=123&bar=456'
    ];
    
    //expected results
    var expected = [
        ''        , 'bar=456'         , 'bar=456'         , 'abc=789&bar=456'
       ,''        , 'bar=456'         , 'bar=456'         , 'abc=789&bar=456'
       ,''        , 'bar=456'         , 'bar=456'         , 'abc=789&bar=456'
       ,'xfoo'    , 'xfoo&bar=456'    , 'bar=456&xfoo'    , 'abc=789&xfoo&bar=456'
       ,'xfoo='   , 'xfoo=&bar=456'   , 'bar=456&xfoo='   , 'abc=789&xfoo=&bar=456'
       ,'xfoo=123', 'xfoo=123&bar=456', 'bar=456&xfoo=123', 'abc=789&xfoo=123&bar=456'
       ,'foox'    , 'foox&bar=456'    , 'bar=456&foox'    , 'abc=789&foox&bar=456'
       ,'foox='   , 'foox=&bar=456'   , 'bar=456&foox='   , 'abc=789&foox=&bar=456'
       ,'foox=123', 'foox=123&bar=456', 'bar=456&foox=123', 'abc=789&foox=123&bar=456'
    ];
    
    for(var i = 0; i < tests.length; i++) {
        var output = tests[i].replace(regex, '');
        var success = (output == expected[i]);
        
        $('#output').append(
            '<tr class="' + (success ? 'passed' : 'failed') + '">'
            + '<td>' + (success ? 'PASS' : 'FAIL') + '</td>'
            + '<td>' + escapeHtml(tests[i]) + '</td>'
            + '<td>' + escapeHtml(output) + '</td>'
            + '<td>' + escapeHtml(expected[i]) + '</td>'
            + '</tr>'
        );
    }
    
});
#output {
    border-collapse: collapse;
    
}
#output tr.passed { background-color: #af8; }
#output tr.failed { background-color: #fc8; }
#output td, #output th {
    border: 1px solid black;
    padding: 2px;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<table id="output">
    <tr>
        <th>Succ?</th>
        <th>Input</th>
        <th>Output</th>
        <th>Expected</th>
    </tr>
</table>

8 个答案:

答案 0 :(得分:21)

如果您只想在一个正则表达式中执行此操作,则可以执行以下操作:

/&foo(=[^&]*)?|^foo(=[^&]*)?&?/

这是因为你需要在foo = ...之前匹配一个&符号,或者在一个之后匹配,或者两个都不匹配,但不能两者都匹配。

老实说,我认为你做的方式更好:在一个单独的步骤中删除尾随的&符号。

答案 1 :(得分:6)

/(?<=&|\?)foo(=[^&]*)?(&|$)/

使用lookbehind和最后一组来“锚定”匹配,并允许缺少值。如果您已从查询字符串中删除了问号,请将\?更改为^

然而,正则表达式仍然不能替代查询字符串的真正解析器。

更新:测试脚本:(在codepad.org运行)

import re

regex = r"(^|(?<=&))foo(=[^&]*)?(&|$)"

cases = {
  "foo=123": "",
  "foo=123&bar=456": "bar=456",
  "bar=456&foo=123": "bar=456",
  "abc=789&foo=123&bar=456": "abc=789&bar=456",

  "oopsfoo=123": "oopsfoo=123",
  "oopsfoo=123&bar=456": "oopsfoo=123&bar=456",
  "bar=456&oopsfoo=123": "bar=456&oopsfoo=123",
  "abc=789&oopsfoo=123&bar=456": "abc=789&oopsfoo=123&bar=456",

  "foo": "",
  "foo&bar=456": "bar=456",
  "bar=456&foo": "bar=456",
  "abc=789&foo&bar=456": "abc=789&bar=456",

  "foo=": "",
  "foo=&bar=456": "bar=456",
  "bar=456&foo=": "bar=456",
  "abc=789&foo=&bar=456": "abc=789&bar=456",
}

failures = 0
for input, expected in cases.items():
  got = re.sub(regex, "", input)
  if got != expected:
    print "failed: input=%r expected=%r got=%r" % (input, expected, got)
    failures += 1
if not failures:
  print "Success"

它显示了我的方法失败的地方,马克有权利 - 这应该说明为什么你不应该用正则表达式做这个...:P


问题是将查询参数与一个&符号相关联,并且 - 如果你必须使用正则表达式(如果你还没有选择它:P,我会使用一个单独的解析器,它可能在其中使用正则表达式,但实际上仍然理解格式) - 一个解决方案是确保每个参数只有一个&符号:用?替换前导&

这给了/&foo(=[^&]*)?(?=&|$)/,这是非常直接的,你将获得最好的。删除最终结果中的前导&(或将其更改回?等)。修改测试用例以执行此操作使用与上述相同的情况,并将循环更改为:

failures = 0
for input, expected in cases.items():
  input = "&" + input
  got = re.sub(regex, "", input)
  if got[:1] == "&":
    got = got[1:]
  if got != expected:
    print "failed: input=%r expected=%r got=%r" % (input, expected, got)
    failures += 1
if not failures:
  print "Success"

答案 2 :(得分:4)

拥有以&开头的查询字符串是无害的 - 为什么不这样做呢?在任何情况下,我建议您搜索尾随的&符号并使用\b来匹配foo的开头w / o接收前一个字符:

 /\bfoo\=[^&]+&?/

答案 3 :(得分:1)

这有点傻但是我开始尝试使用正则表达式来解决这个问题,并希望最终让它工作:)

$str[] = 'foo=123';
$str[] = 'foo=123&bar=456';
$str[] = 'bar=456&foo=123';
$str[] = 'abc=789&foo=123&bar=456';

foreach ($str as $string) {
    echo preg_replace('#(?:^|\b)(&?)foo=[^&]+(&?)#e', "'$1'=='&' && '$2'=='&' ? '&' : ''", $string), "\n";
}

替换部分搞砸了,因为如果捕获的字符是'&' s

,显然会感到困惑

此外,匹配afoo之类的内容。

答案 4 :(得分:1)

感谢。是的,它使用反斜杠进行转义,你是对的,我不需要/。

这似乎有效,但它没有按照原始问题的要求在一行中完成。

    public static string RemoveQueryStringParameter(string url, string keyToRemove)
    {
        //if first parameter, leave ?, take away trailing &
        string pattern = @"\?" + keyToRemove + "[^&]*&?"; 
        url = Regex.Replace(url, pattern, "?");
        //if subsequent parameter, take away leading &
        pattern = "&" + keyToRemove + "[^&]*"; 
        url =  Regex.Replace(url, pattern, "");
        return url;
    }

答案 5 :(得分:1)

我基于您的实现来获得似乎有用的Java impl:

  public static String removeParameterFromQueryString(String queryString,String paramToRemove) {
    Preconditions.checkArgument(queryString != null,"Empty querystring");
    Preconditions.checkArgument(paramToRemove != null,"Empty param");
    String oneParam = "^"+paramToRemove+"(=[^&]*)$";
    String begin = "^"+paramToRemove+"(=[^&]*)(&?)";
    String end = "&"+paramToRemove+"(=[^&]*)$";
    String middle = "(?<=[&])"+paramToRemove+"(=[^&]*)&";
    String removedMiddleParams = queryString.replaceAll(middle,"");
    String removedBeginParams = removedMiddleParams.replaceAll(begin,"");
    String removedEndParams = removedBeginParams.replaceAll(end,"");
    return removedEndParams.replaceAll(oneParam,"");
  }

在某些情况下,我的实施遇到了麻烦,因为有时候它没有删除&,而是通过多个步骤来实现,这似乎更容易理解。

我的版本存在问题,特别是当参数字符串多次出现时(例如param1 = toto&amp; param2 = xxx&amp; param1 = YYY&amp; param3 = ZZZ&amp; param1 ....)

答案 6 :(得分:0)

您可以使用以下正则表达式:

[\?|&](?<name>.*?)=[^&]*&?

如果您想进行完全匹配,可以使用url参数替换(?<name>.*?)。 e.g:

[\?|&]foo=[^&]*&?

匹配任何网址中的任何变量,例如foo=xxxx

答案 7 :(得分:-2)

对于有兴趣替换GET请求参数的任何人:

以下正则表达式适用于更一般的GET方法查询(以?开头),如果要删除的参数是第一个(在?之后),则标记的答案会失败。​​

这个(JS风格)正则表达式可用于删除参数,无论位置(第一个,最后一个,或之间),使查询处于良好格式状态。

所以只需使用空字符串替换正则表达式。

/&s=[^&]*()|\?s=[^&]*$|s=[^&]*&/

基本上它与上述三种情况中的一种(因此是2个管道)相匹配