Normal ifade değişmezlerini JavaScript'te nasıl birleştirebilirim?


145

Böyle bir şey yapmak mümkün mü?

var pattern = /some regex segment/ + /* comment here */
    /another segment/;

Yoksa yeni bir RegExp()sözdizimi kullanmalı ve bir dizeyi birleştirmeliyim? Kod hem daha açık hem de özlü olduğu için değişmez kelimeyi kullanmayı tercih ederim.


2
String.raw () kullanırsanız kaçan normal ifade karakterleriyle uğraşmak daha kolaydır:let regexSegment1 = String.raw`\s*hello\s*`
iono

Yanıtlar:


190

Normal ifade değişmez sözdizimini kullanmadan nasıl düzenli ifade oluşturacağınız aşağıda açıklanmıştır. Bu, normal ifade nesnesi haline gelmeden önce arbitary dize manipülasyonu yapmanızı sağlar:

var segment_part = "some bit of the regexp";
var pattern = new RegExp("some regex segment" + /*comment here */
              segment_part + /* that was defined just now */
              "another segment");

İki normal ifade değişmeziniz varsa, aslında bu tekniği kullanarak bunları birleştirebilirsiniz:

var regex1 = /foo/g;
var regex2 = /bar/y;
var flags = (regex1.flags + regex2.flags).split("").sort().join("").replace(/(.)(?=.*\1)/g, "");
var regex3 = new RegExp(expression_one.source + expression_two.source, flags);
// regex3 is now /foobar/gy

Sadece bir ve iki ifadenin değişmez düzenli ifadeler yerine değişmez dizge olmaktan daha garip.


2
Bu yaklaşımı kullanırken her bir segmentin geçerli bir düzenli ifade olması gerektiğini unutmayın. Gibi bir ifade new RegExp(/(/.source + /.*/.source + /)?/.source);oluşturmak işe yaramıyor gibi görünüyor.
Sam

Bu çözüm, geri eşleşen gruplar durumunda çalışmaz. Bu durumda çalışan bir çözüm için cevabımı görün.
Mikaël Mayer

Bir karakterden kaçmanız gerekiyorsa, çift ters eğik çizgi kullanın: new Regexp ('\\ $' + "flum")
Jeff Lowery

Eğer bayraklara "<regexp> .flags" ile erişmeniz gerekiyorsa, teorik olarak bunları da birleştirebilirsiniz.
bnunamak

Nereden geliyorsun expression_one? Bunu mu demek istediniz regex1?
TallOrderDev

30

Düzenli ifadeler nesnelerini rastgele birleştirmenin bazı olumsuz yan etkileri olabilir. Kullanım RegExp.source yerine:

var r1 = /abc/g;
var r2 = /def/;
var r3 = new RegExp(r1.source + r2.source, 
                   (r1.global ? 'g' : '') 
                   + (r1.ignoreCase ? 'i' : '') + 
                   (r1.multiline ? 'm' : ''));
console.log(r3);
var m = 'test that abcdef and abcdef has a match?'.match(r3);
console.log(m);
// m should contain 2 matches

Bu aynı zamanda standart RegExp bayraklarını kullanarak önceki bir RegExp normal ifade bayraklarını tutma olanağı verecektir.

jsFiddle


Bu kullanılarak geliştirilebilirRegExp.prototype.flags
Dmitry Parzhitsky

19

"Eval" seçeneğine tamamen katılmıyorum.

var xxx = /abcd/;
var yyy = /efgh/;
var zzz = new RegExp(eval(xxx)+eval(yyy));

amaçlanan sonuç olmayan "// abcd // efgh //" verecektir.

Gibi kaynağı kullanma

var zzz = new RegExp(xxx.source+yyy.source);

"/ abcdefgh /" verecektir ve bu doğrudur.

Mantıksal olarak DEĞERLENDİRMEYE gerek yoktur, ANLATIMINIZI bilirsiniz. Sadece KAYNAĞINA ya da nasıl yazıldığına mutlaka ihtiyacınız değil. Bayraklara gelince, RegExp'in isteğe bağlı argümanını kullanmanız yeterlidir.

Benim durumumda, ^ ve $ birlikte bitiştirmeye çalışıyorum birkaç ifadede kullanılan sorunu çalıştırın! Bu ifadeler, program boyunca kullanılan dilbilgisi filtreleridir. Şimdi HAZIRLIK vakalarını ele almak için bazılarını birlikte kullanmak istemiyorum. Başlangıç ​​ve bitiş ^ (ve / veya) $ :) kaldırmak için kaynakları "dilim" gerekebilir Şerefe, Alex.


Source-property kullanımını seviyorum. Eğer benim gibi - jslint kullanın eğer böyle bir şey yaparsanız nag olacak:var regex = "\.\..*"
Nils-o-mat

7

Sorun Normal ifade \ 1 gibi arka eşleme grupları içeriyorsa.

var r = /(a|b)\1/  // Matches aa, bb but nothing else.
var p = /(c|d)\1/   // Matches cc, dd but nothing else.

O zaman sadece kaynakları sağlamlaştırmak işe yaramaz. Aslında, ikisinin kombinasyonu:

var rp = /(a|b)\1(c|d)\1/
rp.test("aadd") // Returns false

Çözüm: İlk olarak, ilk regex'te eşleşen grupların sayısını sayıyoruz, Sonra ikincideki her bir geri eşleme jetonu için, eşleşen grupların sayısına göre artırıyoruz.

function concatenate(r1, r2) {
  var count = function(r, str) {
    return str.match(r).length;
  }
  var numberGroups = /([^\\]|^)(?=\((?!\?:))/g; // Home-made regexp to count groups.
  var offset = count(numberGroups, r1.source);    
  var escapedMatch = /[\\](?:(\d+)|.)/g;        // Home-made regexp for escaped literals, greedy on numbers.
  var r2newSource = r2.source.replace(escapedMatch, function(match, number) { return number?"\\"+(number-0+offset):match; });
  return new RegExp(r1.source+r2newSource,
      (r1.global ? 'g' : '') 
      + (r1.ignoreCase ? 'i' : '')
      + (r1.multiline ? 'm' : ''));
}

Ölçek:

var rp = concatenate(r, p) // returns  /(a|b)\1(c|d)\2/
rp.test("aadd") // Returns true

2
Evet (burada değiştirmeyeceğim). Bu işlev ilişkilendirilebilir, bu nedenle aşağıdaki kodu kullanabilirsiniz:function concatenateList() { var res = arguments[0]; for(var i = 1; i < arguments.length; i++) { res = concatenate(res, arguments[i]); } return res; }
Mikaël Mayer

3

Değişmez sözdiziminin mümkün olduğunca sık kullanılması tercih edilir. Daha kısa, daha okunaklı ve kaçış tırnak işaretleri veya çift kaçış boşluklarına ihtiyacınız yok. "Javascript Kalıpları", Stoyan Stefanov 2010.

Ancak bitiştirmenin tek yolu Yeni'yi kullanmak olabilir.

Değerlendirmeden kaçınırdım. Güvenli değil.


1
Bence karmaşık düzenli ifadeler, sorudaki gibi bölünüp yorumlandığında daha okunaklı.
Sam

3

Şartıyla:

  • normal ifadenizde ne yaptığınızı biliyorsunuz;
  • bir desen oluşturmak için birçok regex parçanız var ve bunlar aynı bayrağı kullanacak;
  • küçük desen parçalarınızı bir diziye ayırmanın daha okunabilir olduğunu görürsünüz;
  • ayrıca bir sonraki geliştirici veya daha sonra kendiniz için her bir parçayı yorumlamak istersiniz;
  • regex'inizi görsel olarak basitleştirmek /this/gyerine new RegExp('this', 'g');
  • regex'i en başından itibaren tek bir parçaya eklemek yerine ekstra bir adımda birleştirmeniz uygundur;

O zaman bu şekilde yazmak isteyebilirsiniz:

var regexParts =
    [
        /\b(\d+|null)\b/,// Some comments.
        /\b(true|false)\b/,
        /\b(new|getElementsBy(?:Tag|Class|)Name|arguments|getElementById|if|else|do|null|return|case|default|function|typeof|undefined|instanceof|this|document|window|while|for|switch|in|break|continue|length|var|(?:clear|set)(?:Timeout|Interval))(?=\W)/,
        /(\$|jQuery)/,
        /many more patterns/
    ],
    regexString  = regexParts.map(function(x){return x.source}).join('|'),
    regexPattern = new RegExp(regexString, 'g');

daha sonra şöyle bir şey yapabilirsiniz:

string.replace(regexPattern, function()
{
    var m = arguments,
        Class = '';

    switch(true)
    {
        // Numbers and 'null'.
        case (Boolean)(m[1]):
            m = m[1];
            Class = 'number';
            break;

        // True or False.
        case (Boolean)(m[2]):
            m = m[2];
            Class = 'bool';
            break;

        // True or False.
        case (Boolean)(m[3]):
            m = m[3];
            Class = 'keyword';
            break;

        // $ or 'jQuery'.
        case (Boolean)(m[4]):
            m = m[4];
            Class = 'dollar';
            break;

        // More cases...
    }

    return '<span class="' + Class + '">' + m + '</span>';
})

Benim özel durumumda (kod ayna benzeri bir editör), bir ifadeyi sarmak için her bir html etiketi ile değiştirdiğimde aşağıdaki gibi birçok yerine büyük bir regex gerçekleştirmek çok daha kolay, bir sonraki desen html etiketinin kendisini etkilemeden (ve maalesef javascript'te desteklenmeyen iyi bir görünüm olmadan) hedeflemek daha zor olabilir :

.replace(/(\b\d+|null\b)/g, '<span class="number">$1</span>')
.replace(/(\btrue|false\b)/g, '<span class="bool">$1</span>')
.replace(/\b(new|getElementsBy(?:Tag|Class|)Name|arguments|getElementById|if|else|do|null|return|case|default|function|typeof|undefined|instanceof|this|document|window|while|for|switch|in|break|continue|var|(?:clear|set)(?:Timeout|Interval))(?=\W)/g, '<span class="keyword">$1</span>')
.replace(/\$/g, '<span class="dollar">$</span>')
.replace(/([\[\](){}.:;,+\-?=])/g, '<span class="ponctuation">$1</span>')

2

Şöyle bir şey yapabilirsiniz:

function concatRegex(...segments) {
  return new RegExp(segments.join(''));
}

Segmentler, bağımsız değişkenler olarak aktarılan dizeler (normal ifade değişmez değerleri yerine) olacaktır.


1

Hayır, gerçek yol desteklenmez. RegExp kullanmanız gerekecek.


1

Yapıcıyı 2 parametre ile kullanın ve '/' sonundaki sorundan kaçının:

var re_final = new RegExp("\\" + ".", "g");    // constructor can have 2 params!
console.log("...finally".replace(re_final, "!") + "\n" + re_final + 
    " works as expected...");                  // !!!finally works as expected

                         // meanwhile

re_final = new RegExp("\\" + "." + "g");              // appends final '/'
console.log("... finally".replace(re_final, "!"));    // ...finally
console.log(re_final, "does not work!");              // does not work

1

Normal ifade kaynağını hem değişmez hem de RegExp sınıfından birleştirebilirsiniz:

var xxx = new RegExp(/abcd/);
var zzz = new RegExp(xxx.source + /efgh/.source);

1

benim için daha kolay yol kaynakları birleştirmek olurdu, örn .:

a = /\d+/
b = /\w+/
c = new RegExp(a.source + b.source)

c değeri şunlarla sonuçlanır:

/ \ D + \ w + /


-2

Ben kullanmayı tercih eval('your expression')o eklemez çünkü /her ucunda yapar./='new RegExp'

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.