استبدال أقصى مثيل regex مباراة في جافا سكريبت

StackOverflow https://stackoverflow.com/questions/36183

  •  09-06-2019
  •  | 
  •  

سؤال

أنا أحاول أن أكتب regex وظيفة التي من شأنها تحديد محل مثيل واحد من مباراة ضمن سلسلة دون أن يؤثر ذلك في حالات أخرى.على سبيل المثال لدي هذه السلسلة:

12||34||56

أريد أن تحل محل المجموعة الثانية من الأنابيب مع اقحام للحصول على هذه السلسلة:

12||34&&56

على regex وظيفة يجب أن يكون قادرا على التعامل مع x كمية من الأنابيب و اسمحوا لي أن محل nth مجموعة من الأنابيب ، لذا يمكن استخدام نفس الدالة لجعل هذه البدائل:

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

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

وأنا أعلم أنني يمكن أن مجرد تقسيم/استبدال/concat السلسلة في الأنابيب ، و أعلم أيضا أنه لا يمكن أن تتطابق على /\|\|/ و تكرار من خلال المصفوفة الناتجة ، ولكن أنا مهتم لمعرفة ما إذا كان من الممكن أن أكتب تعبير واحد التي يمكن أن تفعل هذا.علما أن هذا من شأنه أن يكون جافا سكريبت, لذلك فمن الممكن أن تولد regex في وقت التشغيل باستخدام eval(), لكنه ليس من الممكن استخدام أي بيرل محددة regex التعليمات.

هل كانت مفيدة؟

المحلول

هنا شيء أن يعمل:

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

حيث n هو أقل من أقصى الأنابيب ، (بالطبع أنت لا تحتاج إلى أن أول subexpression إذا كان n = 0)

وإذا كنت ترغب في وظيفة للقيام بذلك:

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

نصائح أخرى

أكثر الأغراض العامة وظيفة

جئت عبر هذا السؤال ، على الرغم من أن العنوان هو عام جدا ، الجواب المقبول يعالج فقط مسألة محددة في حالة استخدام.

أنا في حاجة إلى المزيد من الأغراض العامة الحل ، لذلك كتبت واحد و فكرت مشاركتها هنا.

الاستخدام

هذه الوظيفة يتطلب أن تمر عليه الحجج التالية:

  • original:سلسلة كنت تبحث في
  • pattern:إما سلسلة إلى البحث عن أو RegExp مع القبض على المجموعة.دون القبض على المجموعة ، فإنه سيتم رمي خطأ.وذلك لأن وظيفة المكالمات split على سلسلة الأصلي ، إلا إذا زودت RegExp يحتوي على التقاط مجموعة الناتجة مجموعة تحتوي على المباريات.
  • n:ترتيبي حدوث لايجاد ؛ على سبيل المثال, إذا كنت تريد 2nd المباراة تمر في 2
  • replace:إما سلسلة إلى محل تطابق مع أو وظيفة والتي سوف تأخذ في المباراة والعودة سلسلة الاستبدال.

أمثلة

// 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"

رمز

  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('');

  }

طريقة بديلة لتحديد ذلك

على الأقل جاذبية جزء من هذه الوظيفة هو أن يستغرق 4 الحجج.يمكن أن تكون مبسطة إلى تحتاج فقط 3 الحجج عن طريق إضافته كوسيلة السلسلة النموذج, مثل هذا:

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

إذا كنت تفعل ذلك, يمكنك استدعاء الأسلوب على أي سلسلة, مثل هذا:

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

اجتياز اختبارات

التالية هي الياسمين الاختبارات أن هذه الوظيفة يمر.

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]");

    });

  });

});

قد لا تعمل في IE7

هذا الرمز قد تفشل في IE7 لأن هذا المتصفح بشكل غير صحيح انشقاقات السلاسل باستخدام التعابير المنطقية ، كما نوقش هنا.[يهز قبضة في IE7].وأعتقد أن هذا هو الحل ، إذا كنت بحاجة إلى دعم IE7, حظا سعيدا.:)

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;
        }
    });
}
مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top