web-dev-qa-db-ja.com

クエリ文字列から1つのパラメーターを削除する正規表現

クエリ文字列から単一のパラメーターを削除する正規表現を探しています。可能であれば、単一の正規表現でそれを実行したいと考えています。

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

編集する

コメントで指摘したように、当初考えていたよりもはるかに多くのEdgeケースがあります。私はそれらすべてで動作するように次の正規表現を得ました:

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

これは Mark Byersの回答 から変更されたので、これを受け入れますが、Roger Pateの入力も非常に役立ちました。

以下は、私が使用しているテストケースの完全なスイートと、それらをテストする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>
32
Kip

これを1つの正規表現で行う場合は、次のようにします。

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

これは、foo = ...の前のアンパサンド、またはその後のいずれか、または両方に一致する必要があるためです。

正直なところ、別の手順で末尾のアンパサンドを削除する方がいいと思います。

21
Mark Byers
_/(?<=&|\?)foo(=[^&]*)?(&|$)/
_

後読みと最後のグループを使用して一致を「アンカー」し、欠損値を許可します。クエリ文字列から疑問符を既に取り除いている場合は、_\?_を_^_に変更します。

ただし、正規表現はまだクエリ文字列の実際のパーサーの代わりにはなりません。

更新:テストスクリプト:(- 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


問題は、クエリパラメーターを1つのアンパサンドに関連付けることです。正規表現を使用する必要がある場合(それを取得していない場合:P)、別のパーサーを使用します。形式を理解する)—1つの解決策は、パラメータごとにアンパサンドが1つだけであることを確認することです。先頭の_?_を_&_に置き換えます。

これにより/&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"
_
6
Roger Pate

&で始まるクエリ文字列は無害です-そのままにしないでください。いずれにせよ、末尾のアンパサンドを検索し、\bを使用して、前の文字を取り込んでいないfooの先頭に一致させることをお勧めします。

 /\bfoo\=[^&]+&?/
4
JSBձոգչ

ありがとう。はい、エスケープにバックスラッシュを使用しています。そうです、私は/は必要ありません。

これはうまくいくようですが、元の質問で要求されているように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;
    }
1
Adeel

私はあなたの実装に基づいて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,"");
  }

場合によっては&が削除されず、理解しやすいと思われる複数のステップを実行したため、実装に問題が発生したことがあります。

特に、paramがクエリ文字列に複数回含まれている場合(param1 = toto&param2 = xxx&param1 = YYY&param3 = ZZZ&param1 ...など)、バージョンに問題がありました。

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などと一致します。

1
Matteo Riva

次の正規表現を使用できます。

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

完全に一致させたい場合は、(?<name>.*?)をurlパラメータに置き換えることができます。例えば。:

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

uRL内のfoo=xxxxのような変数に一致します。

0
Sujit Rai