Uzun düzenli bir ifadeyi JavaScript'te birden çok satıra nasıl bölerim?


138

JSLint kurallarına göre her satır uzunluğu 80 karakter tutmak için JavaScript kodumda birden çok satıra bölmek istiyorum çok uzun bir düzenli ifade var. Bence okumak daha iyi. İşte desen örneği:

var pattern = /^(([^<>()[\]\\.,;:\s@\"]+(\.[^<>()[\]\\.,;:\s@\"]+)*)|(\".+\"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/;

4
Görünüşe göre e-posta adreslerini doğrulamaya çalışıyorsunuz. Neden basit değil /\S+@\S+\.\S+/?
Bart Kiers

1
Muhtemelen bunu normal bir ifade olmadan veya birden çok daha küçük düzenli ifade ile yapmanın bir yolunu bulmalısınız. Bu, uzun süren normal bir ifadeden çok daha okunaklı olurdu. Normal ifadeniz yaklaşık 20 karakterden fazlaysa, bunu yapmanın daha iyi bir yolu olabilir.
ForbesLindesay

2
Günümüzde 80 karakter geniş monitörlerle modası geçmiş değil mi?
Oleg V. Volkov

7
Bir kişi bir sunucu odasında sanal bir terminal olan vim'de bölünmüş pencereler kullanıyor olabilir. Herkesin sizinle aynı görünümde kodlayacağını varsaymak yanlıştır. Ayrıca, satırlarınızı 80 karakterle sınırlamak, kodunuzu daha küçük işlevlere bölmeye zorlar.
synic

Kesinlikle burada bunu yapmak için motivasyonunuzu görüyorum - bu normal ifade Koolilnc tarafından gösterildiği gibi birden fazla satıra bölünür, hemen okunabilir, kendi kendini belgeleyen kodun mükemmel bir örneği haline gelir. Am_¬
Mark Amery

Yanıtlar:


115

Bir dizeye dönüştürebilir ve şu ifadeyi kullanarak ifadeyi oluşturabilirsiniz new RegExp():

var myRE = new RegExp (['^(([^<>()[\]\\.,;:\\s@\"]+(\\.[^<>(),[\]\\.,;:\\s@\"]+)*)',
                        '|(\\".+\\"))@((\\[[0-9]{1,3}\\.[0-9]{1,3}\\.[0-9]{1,3}\\.',
                        '[0-9]{1,3}\])|(([a-zA-Z\-0-9]+\\.)+',
                        '[a-zA-Z]{2,}))$'].join(''));

Notlar:

  1. ifade değişmezini bir dizeye dönüştürürken, bir dize değişmezini değerlendirirken ters eğik çizgiler kullanıldığından tüm ters eğik çizgilerden kaçmanız gerekir . (Daha fazla ayrıntı için Kayo'nun yorumuna bakın.)
  2. RegExp değiştiricileri ikinci bir parametre olarak kabul eder

    /regex/g => new RegExp('regex', 'g')

[ ES20xx eki (etiketli şablon)]

ES20xx'de etiketli şablonları kullanabilirsiniz . Snippet'e bakın.

Not:

  • Burada dezavantaj sen (her zaman kullanmak düzenli ifade dizesinde düz boşluk kullanamazsınız olmasıdır \s, \s+, \s{1,x}, \t, \nvb.)

(() => {
  const createRegExp = (str, opts) => 
    new RegExp(str.raw[0].replace(/\s/gm, ""), opts || "");
  const yourRE = createRegExp`
    ^(([^<>()[\]\\.,;:\s@\"]+(\.[^<>()[\]\\.,;:\s@\"]+)*)|
    (\".+\"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\])|
    (([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$`;
  console.log(yourRE);
  const anotherLongRE = createRegExp`
    (\byyyy\b)|(\bm\b)|(\bd\b)|(\bh\b)|(\bmi\b)|(\bs\b)|(\bms\b)|
    (\bwd\b)|(\bmm\b)|(\bdd\b)|(\bhh\b)|(\bMI\b)|(\bS\b)|(\bMS\b)|
    (\bM\b)|(\bMM\b)|(\bdow\b)|(\bDOW\b)
    ${"gi"}`;
  console.log(anotherLongRE);
})();


4
A new RegExp, çok satırlı düzenli ifadeler için harika bir yoldur. var reg = new RegExp('^([a-' + 'z]+)$','i');
Dizilere

43
Dikkat: Uzun bir düzenli ifade değişmez değeri, yukarıdaki yanıt kullanılarak birden çok satıra bölünebilir. Ancak, düzenli ifade değişmezini (ile tanımlanmış //) kopyalayamazsınız ve RegExp yapıcısına dize bağımsız değişkeni olarak yapıştıramazsınız. Bunun nedeni, dize değişmez değeri değerlendirilirken ters eğik çizgi karakterlerinin tüketilmesidir . Örnek: /Hey\sthere/ile değiştirilemez new RegExp("Hey\sthere"). Bunun yerine new RegExp("Hey\\sthere")Ekstra ters eğik çizgi Not! Bu nedenle, uzun bir satırda uzun bir normal ifade değişkeni bırakmayı tercih ediyorum
Kayo

5
Bunu yapmanın daha da net bir yolu, anlamlı alt bölümler tutan ve bunları dize veya bir dizide birleştiren adlandırılmış değişkenler oluşturmaktır . Bu RegExp, anlaşılması çok daha kolay bir şekilde oluşturmanıza olanak tanır .
Chris Krycho

118

@KooiInc yanıtını genişleterek source, RegExpnesnenin özelliğini kullanarak her özel karakterin el ile kaçmasını önleyebilirsiniz .

Misal:

var urlRegex= new RegExp(''
  + /(?:(?:(https?|ftp):)?\/\/)/.source     // protocol
  + /(?:([^:\n\r]+):([^@\n\r]+)@)?/.source  // user:pass
  + /(?:(?:www\.)?([^\/\n\r]+))/.source     // domain
  + /(\/[^?\n\r]+)?/.source                 // request
  + /(\?[^#\n\r]*)?/.source                 // query
  + /(#?[^\n\r]*)?/.source                  // anchor
);

veya .sourceözelliği tekrarlamaktan kaçınmak istiyorsanız, şu Array.map()işlevi kullanarak yapabilirsiniz :

var urlRegex= new RegExp([
  /(?:(?:(https?|ftp):)?\/\/)/      // protocol
  ,/(?:([^:\n\r]+):([^@\n\r]+)@)?/  // user:pass
  ,/(?:(?:www\.)?([^\/\n\r]+))/     // domain
  ,/(\/[^?\n\r]+)?/                 // request
  ,/(\?[^#\n\r]*)?/                 // query
  ,/(#?[^\n\r]*)?/                  // anchor
].map(function(r) {return r.source}).join(''));

ES6'da harita işlevi aşağıdakilere indirilebilir: .map(r => r.source)


3
Tam olarak ne aradığım, süper temiz. Teşekkürler!
Marian Zagoruiko

10
Bu, uzun bir normal ifadeye yorum eklemek için gerçekten kullanışlıdır. Ancak, aynı satırda eşleşen parantez bulunmasıyla sınırlıdır.
Nathan S. Watson-Haigh

Kesinlikle, bu! Her alt regex yorum yeteneği ile süper güzel.
GaryO

Teşekkürler, regex fonksiyonunda kaynak koymaya yardımcı oldu
Kod

Çok zeki. Teşekkürler, bu fikir bana çok yardımcı oldu. Tıpkı bir yan not olarak: Daha da temiz hale getirmek için her şeyi bir fonksiyonda kapsülledim: combineRegex = (...regex) => new RegExp(regex.map(r => r.source).join(""))Kullanım:combineRegex(/regex1/, /regex2/, ...)
Scindix

25

Dizeleri kullanmak new RegExpgariptir, çünkü tüm ters eğik çizgilerden kaçmanız gerekir. Daha küçük düzenli ifadeler yazabilir ve bunları birleştirebilirsiniz.

Hadi bu normal ifadeyi bölelim

/^foo(.*)\bar$/

Daha sonra işleri daha güzel hale getirmek için bir işlev kullanacağız

function multilineRegExp(regs, options) {
    return new RegExp(regs.map(
        function(reg){ return reg.source; }
    ).join(''), options);
}

Ve şimdi sallayalım

var r = multilineRegExp([
     /^foo/,  // we can add comments too
     /(.*)/,
     /\bar$/
]);

Bir maliyeti olduğundan, gerçek regex'i sadece bir kez oluşturmaya çalışın ve sonra bunu kullanın.


Bu çok güzel - sadece ek kaçış yapmak zorunda değilsiniz, aynı zamanda alt regex'ler için özel sözdizimi vurgusunu da korursunuz!
quezak

yine de bir uyarı: alt regex'lerinizin bağımsız olduğundan emin olmanız veya her birini yeni bir parantez grubuna sarmanız gerekir. Örnek: kastettiğiniz halde multilineRegExp([/a|b/, /c|d])sonuçlanır . /a|bc|d/(a|b)(c|d)
quezak

6

Burada iyi cevaplar var, ancak tamlık için birisi Javascript'in prototip zinciri ile kalıtımın temel özelliğinden bahsetmelidir . Böyle bir şey fikri gösterir:

RegExp.prototype.append = function(re) {
  return new RegExp(this.source + re.source, this.flags);
};

let regex = /[a-z]/g
.append(/[A-Z]/)
.append(/[0-9]/);

console.log(regex); //=> /[a-z][A-Z][0-9]/g


Buradaki en iyi cevap bu.
parttimeturtle

6

Şablon değişmezlerinin harika dünyası sayesinde artık ES6'da büyük, çok satırlı, iyi yorumlanmış ve hatta anlamsal olarak iç içe regex'ler yazabilirsiniz.

//build regexes without worrying about
// - double-backslashing
// - adding whitespace for readability
// - adding in comments
let clean = (piece) => (piece
    .replace(/((^|\n)(?:[^\/\\]|\/[^*\/]|\\.)*?)\s*\/\*(?:[^*]|\*[^\/])*(\*\/|)/g, '$1')
    .replace(/((^|\n)(?:[^\/\\]|\/[^\/]|\\.)*?)\s*\/\/[^\n]*/g, '$1')
    .replace(/\n\s*/g, '')
);
window.regex = ({raw}, ...interpolations) => (
    new RegExp(interpolations.reduce(
        (regex, insert, index) => (regex + insert + clean(raw[index + 1])),
        clean(raw[0])
    ))
);

Bunu kullanarak artık aşağıdaki gibi normal ifadeler yazabilirsiniz:

let re = regex`I'm a special regex{3} //with a comment!`;

çıktılar

/I'm a special regex{3}/

Ya da çok satırlı?

'123hello'
    .match(regex`
        //so this is a regex

        //here I am matching some numbers
        (\d+)

        //Oh! See how I didn't need to double backslash that \d?
        ([a-z]{1,3}) /*note to self, this is group #2*/
    `)
    [2]

Çıktılar hel, temiz!
"Ne Aslında bir yeni satır aramanıza gerek olur?", İyi o kullanmak \nsaçma!
Firefox ve Chrome'um üzerinde çalışıyor.


Tamam, "biraz daha karmaşık bir şeye ne dersin?"
Tabii, işte üzerinde çalıştığım JS ayrıştırıcısını bir nesne parçası :

regex`^\s*
    (
        //closing the object
        (\})|

        //starting from open or comma you can...
        (?:[,{]\s*)(?:
            //have a rest operator
            (\.\.\.)
            |
            //have a property key
            (
                //a non-negative integer
                \b\d+\b
                |
                //any unencapsulated string of the following
                \b[A-Za-z$_][\w$]*\b
                |
                //a quoted string
                //this is #5!
                ("|')(?:
                    //that contains any non-escape, non-quote character
                    (?!\5|\\).
                    |
                    //or any escape sequence
                    (?:\\.)
                //finished by the quote
                )*\5
            )
            //after a property key, we can go inside
            \s*(:|)
      |
      \s*(?={)
        )
    )
    ((?:
        //after closing we expect either
        // - the parent's comma/close,
        // - or the end of the string
        \s*(?:[,}\]=]|$)
        |
        //after the rest operator we expect the close
        \s*\}
        |
        //after diving into a key we expect that object to open
        \s*[{[:]
        |
        //otherwise we saw only a key, we now expect a comma or close
        \s*[,}{]
    ).*)
$`

Çıktı /^\s*((\})|(?:[,{]\s*)(?:(\.\.\.)|(\b\d+\b|\b[A-Za-z$_][\w$]*\b|("|')(?:(?!\5|\\).|(?:\\.))*\5)\s*(:|)|\s*(?={)))((?:\s*(?:[,}\]=]|$)|\s*\}|\s*[{[:]|\s*[,}{]).*)$/

Ve küçük bir demo ile mi çalıştırıyorsunuz?

let input = '{why, hello, there, "you   huge \\"", 17, {big,smelly}}';
for (
    let parsed;
    parsed = input.match(r);
    input = parsed[parsed.length - 1]
) console.log(parsed[1]);

Başarıyla çıktı

{why
, hello
, there
, "you   huge \""
, 17
,
{big
,smelly
}
}

Alıntılanan dizenin başarılı bir şekilde yakalandığına dikkat edin.
Chrome ve Firefox'ta test ettim, bir tedavi yapıyor!

Eğer merak ne yaptığımı ödeme yapabilirsiniz ve onun gösteri .
Firefox geri başvuruları veya adlandırılmış grupları desteklemediğinden, yalnızca Chrome'da çalışıyor olsa da. Bu nedenle, bu cevapta verilen örneğin aslında kısırlaştırılmış bir sürüm olduğunu ve geçersiz dizeleri kabul etmek için kolayca kandırılabileceğini unutmayın.


1
bunu bir NodeJS paketi olarak dışa aktarmayı düşünmelisiniz, harika
rmobis

1
Ben hiç yapmadım, burada oldukça kapsamlı bir öğretici var: zellwk.com/blog/publish-to-npm . Sayfanın sonunda np'yi kontrol etmenizi öneririm. Hiç kullanmadım, ama Sindre Sorhus bu şeylerle bir sihirbaz, bu yüzden onu geçmem.
rmobis

4

Yukarıdaki normal ifadede düzgün çalışmayan bazı siyah eğik çizgiler eksik. Böylece, normal ifadeyi düzenledim. Lütfen e-posta doğrulaması için% 99,99 çalışan bu normal ifadeyi göz önünde bulundurun.

let EMAIL_REGEXP = 
new RegExp (['^(([^<>()[\\]\\\.,;:\\s@\"]+(\\.[^<>()\\[\\]\\\.,;:\\s@\"]+)*)',
                    '|(".+"))@((\\[[0-9]{1,3}\\.[0-9]{1,3}\\.[0-9]{1,3}\\.',
                    '[0-9]{1,3}\])|(([a-zA-Z\\-0-9]+\\.)+',
                    '[a-zA-Z]{2,}))$'].join(''));

1

Diziden kaçınmak için joinaşağıdaki sözdizimini de kullanabilirsiniz:

var pattern = new RegExp('^(([^<>()[\]\\.,;:\s@\"]+' +
  '(\.[^<>()[\]\\.,;:\s@\"]+)*)|(\".+\"))@' +
  '((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\])|' +
  '(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$');

0

Şahsen, daha az karmaşık bir regex için giderdim:

/\S+@\S+\.\S+/

Tabii, şu anki modelinizden daha az doğru , ama ne yapmaya çalışıyorsunuz? Kullanıcılarınızın girebileceği yanlışlıkla hataları mı yakalamaya çalışıyorsunuz, yoksa kullanıcılarınızın geçersiz adresler girmeye çalışacağından mı endişeleniyorsunuz? Eğer bu ilkse, daha kolay bir desen seçerdim. İkincisiyse, o adrese gönderilen bir e-postayı yanıtlayarak yapılan bazı doğrulama daha iyi bir seçenek olabilir.

Ancak, mevcut kalıbınızı kullanmak istiyorsanız, (IMO) daha küçük alt kalıplardan oluşturarak okumak (ve korumak!) Daha kolay olurdu:

var box1 = "([^<>()[\]\\\\.,;:\s@\"]+(\\.[^<>()[\\]\\\\.,;:\s@\"]+)*)";
var box2 = "(\".+\")";

var host1 = "(\\[[0-9]{1,3}\\.[0-9]{1,3}\\.[0-9]{1,3}\\.[0-9]{1,3}\\])";
var host2 = "(([a-zA-Z\-0-9]+\\.)+[a-zA-Z]{2,})";

var regex = new RegExp("^(" + box1 + "|" + box2 + ")@(" + host1 + "|" + host2 + ")$");

21
Downvoting - Normal ifade karmaşıklığını azaltma hakkındaki yorumlarınız geçerli olsa da, OP özellikle "uzun normal ifadeyi birden çok satıra nasıl bölebileceğinizi" soruyor. Bu nedenle, tavsiyeniz geçerli olsa da, yanlış nedenlerle verilmiştir. örneğin, iş mantığını bir programlama dili etrafında değiştirmek için değiştirme. Ayrıca, verdiğiniz kod örneği oldukça çirkin.
sleepycal

4
@sleepycal Sanırım Bart soruyu yanıtladı. Cevabının son bölümüne bakın. Soruyu yanıtladı ve bir alternatif verdi.
Nidhin David

0

Basitçe dize işlemini kullanabilirsiniz.

var pattenString = "^(([^<>()[\]\\.,;:\s@\"]+(\.[^<>()[\]\\.,;:\s@\"]+)*)|"+
"(\".+\"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\])|"+
"(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$";
var patten = new RegExp(pattenString);

0

Her şeyi kapsülleyerek ve yakalama gruplarını ve karakter setlerini bölmek için destek uygulayarak korun yanıtını geliştirmeye çalıştım - bu yöntemi çok yönlü hale getirdim.

Bu snippet'i kullanmak için, argümanlarını combineRegexbirleştirmeniz gereken normal ifade nesneleri olan varyasyon fonksiyonunu çağırmanız gerekir. Uygulanması en altta bulunabilir.

Grupları yakalamak, bir parçayı sadece bir parantez içinde bırakabileceği için doğrudan bu şekilde bölünemez. Tarayıcınız bir istisna dışında başarısız olur.

Bunun yerine, yakalama grubunun içeriğini bir dizinin içine aktarıyorum. combineRegexBir dizi ile karşılaşıldığında parantezler otomatik olarak eklenir .

Ayrıca nicelik belirteçlerin bir şey izlemesi gerekir. Herhangi bir nedenle normal ifadenin bir nicelleştiricinin önüne bölünmesi gerekiyorsa, bir çift parantez eklemeniz gerekir. Bunlar otomatik olarak kaldırılacaktır. Mesele şu ki, boş bir yakalama grubu oldukça işe yaramaz ve bu şekilde niceleyicilerin atıfta bulunacak bir şeyleri vardır. Yakalamayan gruplar ( /(?:abc)/olur [/()?:abc/]) gibi şeyler için de aynı yöntem kullanılabilir .

Bu en iyi basit bir örnek kullanılarak açıklanabilir:

var regex = /abcd(efghi)+jkl/;

olacaktı:

var regex = combineRegex(
    /ab/,
    /cd/,
    [
        /ef/,
        /ghi/
    ],
    /()+jkl/    // Note the added '()' in front of '+'
);

Karakter kümelerini bölmeniz gerekiyorsa {"":[regex1, regex2, ...]}, diziler ( [regex1, regex2, ...]) yerine object ( ) kullanabilirsiniz. Anahtarın içeriği, nesne yalnızca bir anahtar içerdiği sürece herhangi bir şey olabilir. İlk karakterin nicelik belirteci olarak yorumlanabilmesi yerine, bunun yerine kukla başlangıç ​​olarak ()kullanmanız ]gerektiğini unutmayın. Yani /[+?]/olur{"":[/]+?/]}

Snippet ve daha eksiksiz bir örnek:

function combineRegexStr(dummy, ...regex)
{
    return regex.map(r => {
        if(Array.isArray(r))
            return "("+combineRegexStr(dummy, ...r).replace(dummy, "")+")";
        else if(Object.getPrototypeOf(r) === Object.getPrototypeOf({}))
            return "["+combineRegexStr(/^\]/, ...(Object.entries(r)[0][1]))+"]";
        else 
            return r.source.replace(dummy, "");
    }).join("");
}
function combineRegex(...regex)
{
    return new RegExp(combineRegexStr(/^\(\)/, ...regex));
}

//Usage:
//Original:
console.log(/abcd(?:ef[+A-Z0-9]gh)+$/.source);
//Same as:
console.log(
  combineRegex(
    /ab/,
    /cd/,
    [
      /()?:ef/,
      {"": [/]+A-Z/, /0-9/]},
      /gh/
    ],
    /()+$/
  ).source
);


0

@ Hashbrown'un harika yanıtı beni doğru yola soktu. İşte bu blogdan ilham alan versiyonum .

function regexp(...args) {
  function cleanup(string) {
    // remove whitespace, single and multi-line comments
    return string.replace(/\s+|\/\/.*|\/\*[\s\S]*?\*\//g, '');
  }

  function escape(string) {
    // escape regular expression
    return string.replace(/[-.*+?^${}()|[\]\\]/g, '\\$&');
  }

  function create(flags, strings, ...values) {
    let pattern = '';
    for (let i = 0; i < values.length; ++i) {
      pattern += cleanup(strings.raw[i]);  // strings are cleaned up
      pattern += escape(values[i]);        // values are escaped
    }
    pattern += cleanup(strings.raw[values.length]);
    return RegExp(pattern, flags);
  }

  if (Array.isArray(args[0])) {
    // used as a template tag (no flags)
    return create('', ...args);
  }

  // used as a function (with flags)
  return create.bind(void 0, args[0]);
}

Şöyle kullanın:

regexp('i')`
  //so this is a regex

  //here I am matching some numbers
  (\d+)

  //Oh! See how I didn't need to double backslash that \d?
  ([a-z]{1,3}) /*note to self, this is group #2*/
`

Bu RegExpnesneyi oluşturmak için :

/(\d+)([a-z]{1,3})/i
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.