Frage

Ich versuche, eine Regex-Funktion zu schreiben, die eine einzelne Instanz einer Übereinstimmung innerhalb einer Zeichenfolge identifiziert und ersetzt, ohne die anderen Instanzen zu beeinträchtigen.Ich habe zum Beispiel diese Zeichenfolge:

12||34||56

Ich möchte den zweiten Satz Pipes durch kaufmännische Und-Zeichen ersetzen, um diese Zeichenfolge zu erhalten:

12||34&&56

Die Regex-Funktion muss in der Lage sein, eine x-Anzahl von Pipes zu verarbeiten und es mir zu ermöglichen, den n-ten Satz von Pipes zu ersetzen, damit ich dieselbe Funktion verwenden kann, um diese Ersetzungen vorzunehmen:

23||45||45||56||67 -> 23&&45||45||56||67

23||34||98||87 -> 23||34||98&&87

Ich weiß, dass ich die Saite an den Rohren einfach teilen/ersetzen/zusammenfügen könnte, und ich weiß auch, dass ich weitermachen kann /\|\|/ und durch das resultierende Array iterieren, aber ich bin daran interessiert zu wissen, ob es möglich ist, einen einzelnen Ausdruck zu schreiben, der dies kann.Beachten Sie, dass dies für Javascript gilt, sodass es möglich ist, zur Laufzeit einen regulären Ausdruck zu generieren eval(), aber es ist nicht möglich, Perl-spezifische Regex-Anweisungen zu verwenden.

War es hilfreich?

Lösung

Hier ist etwas, das funktioniert:

"23||45||45||56||67".replace(/^((?:[0-9]+\|\|){n})([0-9]+)\|\|/,"$1$2&&")

wobei n die Zahl kleiner als die n-te Pipe ist (natürlich brauchen Sie diesen ersten Unterausdruck nicht, wenn n = 0)

Und wenn Sie möchten, dass eine Funktion dies tut:

function pipe_replace(str,n) {
   var RE = new RegExp("^((?:[0-9]+\\|\\|){" + (n-1) + "})([0-9]+)\|\|");
   return str.replace(RE,"$1$2&&");
}

Andere Tipps

Eine allgemeinere Funktion

Ich bin auf diese Frage gestoßen und obwohl der Titel sehr allgemein ist, behandelt die akzeptierte Antwort nur den spezifischen Anwendungsfall der Frage.

Ich brauchte eine allgemeinere Lösung, also habe ich eine geschrieben und dachte, ich würde sie hier teilen.

Verwendung

Diese Funktion erfordert, dass Sie ihr die folgenden Argumente übergeben:

  • original:die Zeichenfolge, in der Sie suchen
  • pattern:entweder eine Zeichenfolge, nach der gesucht werden soll, oder ein RegExp mit einer Capture-Gruppe.Ohne eine Capture-Gruppe wird ein Fehler ausgegeben.Dies liegt daran, dass die Funktion aufgerufen wird split auf der Originalsaite und Nur wenn der bereitgestellte RegExp eine Capture-Gruppe enthält, enthält das resultierende Array die Übereinstimmungen.
  • n:das zu findende ordinale Vorkommen;Wenn Sie beispielsweise das 2. Spiel wünschen, geben Sie es ein 2
  • replace:Entweder eine Zeichenfolge, durch die die Übereinstimmung ersetzt wird, oder eine Funktion, die die Übereinstimmung aufnimmt und eine Ersatzzeichenfolge zurückgibt.

Beispiele

// Pipe examples like the OP's
replaceNthMatch("12||34||56", /(\|\|)/, 2, '&&') // "12||34&&56"
replaceNthMatch("23||45||45||56||67", /(\|\|)/, 1, '&&') // "23&&45||45||56||67"

// Replace groups of digits
replaceNthMatch("foo-1-bar-23-stuff-45", /(\d+)/, 3, 'NEW') // "foo-1-bar-23-stuff-NEW"

// Search value can be a string
replaceNthMatch("foo-stuff-foo-stuff-foo", "foo", 2, 'bar') // "foo-stuff-bar-stuff-foo"

// No change if there is no match for the search
replaceNthMatch("hello-world", "goodbye", 2, "adios") // "hello-world"

// No change if there is no Nth match for the search
replaceNthMatch("foo-1-bar-23-stuff-45", /(\d+)/, 6, 'NEW') // "foo-1-bar-23-stuff-45"

// Passing in a function to make the replacement
replaceNthMatch("foo-1-bar-23-stuff-45", /(\d+)/, 2, function(val){
  //increment the given value
  return parseInt(val, 10) + 1;
}); // "foo-1-bar-24-stuff-45"

Der Code

  var replaceNthMatch = function (original, pattern, n, replace) {
    var parts, tempParts;

    if (pattern.constructor === RegExp) {

      // If there's no match, bail
      if (original.search(pattern) === -1) {
        return original;
      }

      // Every other item should be a matched capture group;
      // between will be non-matching portions of the substring
      parts = original.split(pattern);

      // If there was a capture group, index 1 will be
      // an item that matches the RegExp
      if (parts[1].search(pattern) !== 0) {
        throw {name: "ArgumentError", message: "RegExp must have a capture group"};
      }
    } else if (pattern.constructor === String) {
      parts = original.split(pattern);
      // Need every other item to be the matched string
      tempParts = [];

      for (var i=0; i < parts.length; i++) {
        tempParts.push(parts[i]);

        // Insert between, but don't tack one onto the end
        if (i < parts.length - 1) {
          tempParts.push(pattern);
        }
      }
      parts = tempParts;
    }  else {
      throw {name: "ArgumentError", message: "Must provide either a RegExp or String"};
    }

    // Parens are unnecessary, but explicit. :)
    indexOfNthMatch = (n * 2) - 1;

  if (parts[indexOfNthMatch] === undefined) {
    // There IS no Nth match
    return original;
  }

  if (typeof(replace) === "function") {
    // Call it. After this, we don't need it anymore.
    replace = replace(parts[indexOfNthMatch]);
  }

  // Update our parts array with the new value
  parts[indexOfNthMatch] = replace;

  // Put it back together and return
  return parts.join('');

  }

Eine alternative Möglichkeit, es zu definieren

Der am wenigsten ansprechende Teil dieser Funktion ist, dass sie 4 Argumente benötigt.Es könnte vereinfacht werden, sodass nur drei Argumente erforderlich sind, indem es wie folgt als Methode zum String-Prototyp hinzugefügt wird:

String.prototype.replaceNthMatch = function(pattern, n, replace) {
  // Same code as above, replacing "original" with "this"
};

Wenn Sie das tun, können Sie die Methode für jede beliebige Zeichenfolge aufrufen, etwa so:

"foo-bar-foo".replaceNthMatch("foo", 2, "baz"); // "foo-bar-baz"

Bestehen von Prüfungen

Im Folgenden sind die Jasmine-Tests aufgeführt, die diese Funktion besteht.

describe("replaceNthMatch", function() {

  describe("when there is no match", function() {

    it("should return the unmodified original string", function() {
      var str = replaceNthMatch("hello-there", /(\d+)/, 3, 'NEW');
      expect(str).toEqual("hello-there");
    });

  });

  describe("when there is no Nth match", function() {

    it("should return the unmodified original string", function() {
      var str = replaceNthMatch("blah45stuff68hey", /(\d+)/, 3, 'NEW');
      expect(str).toEqual("blah45stuff68hey");
    });

  });

  describe("when the search argument is a RegExp", function() {

    describe("when it has a capture group", function () {

      it("should replace correctly when the match is in the middle", function(){
        var str = replaceNthMatch("this_937_thing_38_has_21_numbers", /(\d+)/, 2, 'NEW');
        expect(str).toEqual("this_937_thing_NEW_has_21_numbers");
      });

      it("should replace correctly when the match is at the beginning", function(){
        var str = replaceNthMatch("123_this_937_thing_38_has_21_numbers", /(\d+)/, 2, 'NEW');
        expect(str).toEqual("123_this_NEW_thing_38_has_21_numbers");
      });

    });

    describe("when it has no capture group", function() {

      it("should throw an error", function(){
        expect(function(){
          replaceNthMatch("one_1_two_2", /\d+/, 2, 'NEW');
        }).toThrow('RegExp must have a capture group');
      });

    });


  });

  describe("when the search argument is a string", function() {

    it("should should match and replace correctly", function(){
      var str = replaceNthMatch("blah45stuff68hey", 'stuff', 1, 'NEW');
      expect(str).toEqual("blah45NEW68hey");
    });

  });

  describe("when the replacement argument is a function", function() {

    it("should call it on the Nth match and replace with the return value", function(){

      // Look for the second number surrounded by brackets
      var str = replaceNthMatch("foo[1][2]", /(\[\d+\])/, 2, function(val) {

        // Get the number without the [ and ]
        var number = val.slice(1,-1);

        // Add 1
        number = parseInt(number,10) + 1;

        // Re-format and return
        return '[' + number + ']';
      });
      expect(str).toEqual("foo[1][3]");

    });

  });

});

Funktioniert möglicherweise nicht in IE7

Dieser Code schlägt möglicherweise in IE7 fehl, weil dieser Browser Zeichenfolgen mithilfe eines regulären Ausdrucks falsch aufteilt, wie bereits erläutert Hier.[schüttelt die Faust bei IE7].Ich glaube das Das ist die Lösung;Wenn Sie IE7 unterstützen müssen, viel Glück.:) :)

function pipe_replace(str,n) {
    m = 0;
    return str.replace(/\|\|/g, function (x) {
        //was n++ should have been m++
        m++;
        if (n==m) {
            return "&&";
        } else {
            return x;
        }
    });
}
Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top