Wie kann man einen langen regulären Ausdruck in JavaScript in mehrere Zeilen aufteilen?

138

Ich habe einen sehr langen regulären Ausdruck, den ich in meinem JavaScript-Code in mehrere Zeilen aufteilen möchte, damit jede Zeilenlänge gemäß den JSLint-Regeln 80 Zeichen lang bleibt. Es ist einfach besser zum Lesen, denke ich. Hier ist ein Musterbeispiel:

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,}))$/;
Nik Sumeiko
quelle
4
Anscheinend versuchen Sie, E-Mail-Adressen zu überprüfen. Warum nicht einfach machen /\S+@\S+\.\S+/?
Bart Kiers
1
Sie sollten wahrscheinlich nach einer Möglichkeit suchen, dies ohne einen regulären Ausdruck oder mit mehreren kleineren regulären Ausdrücken zu tun. Das wäre viel besser lesbar als ein so langer regulärer Ausdruck. Wenn Ihr regulärer Ausdruck mehr als 20 Zeichen umfasst, gibt es wahrscheinlich einen besseren Weg, dies zu tun.
ForbesLindesay
2
Sind 80 Zeichen heutzutage mit breiten Monitoren nicht veraltet?
Oleg V. Volkov
7
@ OlegV.Volkov Nein. Eine Person könnte geteilte Fenster in vim verwenden, einem virtuellen Terminal in einem Serverraum. Es ist falsch anzunehmen, dass jeder im selben Ansichtsfenster wie Sie codiert. Wenn Sie Ihre Zeilen auf 80 Zeichen beschränken, müssen Sie Ihren Code in kleinere Funktionen aufteilen.
Synic
Nun, ich sehe sicherlich Ihre Motivation, dies hier tun zu wollen - sobald dieser reguläre Ausdruck, wie von Koolilnc gezeigt, auf mehrere Zeilen aufgeteilt ist, wird er sofort zu einem perfekten Beispiel für lesbaren, selbstdokumentierenden Code. ¬_¬
Mark Amery

Antworten:

115

Sie können es in eine Zeichenfolge konvertieren und den Ausdruck erstellen, indem Sie Folgendes aufrufen 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(''));

Anmerkungen:

  1. Wenn Sie das Ausdrucksliteral in eine Zeichenfolge konvertieren , müssen Sie alle Backslashes maskieren, da bei der Auswertung eines String-Literals Backslashes verwendet werden . (Weitere Informationen finden Sie in Kayos Kommentar.)
  2. RegExp akzeptiert Modifikatoren als zweiten Parameter

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

[ Zusatz ES20xx (getaggte Vorlage)]

In ES20xx können Sie getaggte Vorlagen verwenden . Siehe das Snippet.

Hinweis:

  • Nachteil hier ist , dass man nicht schlicht Leerzeichen im regulären Ausdruck Zeichenfolge (immer verwenden kann \s, \s+, \s{1,x}, \t, \nusw.).

(() => {
  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);
})();

KooiInc
quelle
4
A new RegExpist eine großartige Möglichkeit für mehrzeilige reguläre Ausdrücke. Anstatt Arrays zu verbinden, können Sie einfach einen String-Verkettungsoperator verwenden:var reg = new RegExp('^([a-' + 'z]+)$','i');
Dakab
43
Achtung: Ein langes Literal für reguläre Ausdrücke kann mit der obigen Antwort in mehrere Zeilen unterteilt werden. Es ist jedoch Vorsicht geboten, da Sie das Literal für reguläre Ausdrücke (definiert mit //) nicht einfach kopieren und als Zeichenfolgenargument in den RegExp-Konstruktor einfügen können. Dies liegt daran, dass bei der Auswertung des Zeichenfolgenliterals Backslash-Zeichen verwendet werden . Beispiel: /Hey\sthere/kann nicht ersetzt werden durch new RegExp("Hey\sthere"). Stattdessen sollte es durch new RegExp("Hey\\sthere")Hinweis auf den zusätzlichen Backslash ersetzt werden! Daher ziehe ich es vor, nur ein langes Regex-Literal in einer langen Zeile zu
belassen
5
Eine noch klarere Möglichkeit, dies zu tun, besteht darin, benannte Variablen mit aussagekräftigen Unterabschnitten zu erstellen und diese als Zeichenfolgen oder in einem Array zu verknüpfen. Auf diese Weise können Sie das RegExpauf eine Weise konstruieren, die viel einfacher zu verstehen ist.
Chris Krycho
115

Wenn Sie die Antwort @KooiInc erweitern, können Sie vermeiden, dass jedes Sonderzeichen manuell maskiert wird, indem Sie die sourceEigenschaft des RegExpObjekts verwenden.

Beispiel:

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

oder wenn Sie vermeiden möchten, die .sourceEigenschaft zu wiederholen , können Sie dies mit der folgenden Array.map()Funktion tun :

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

In ES6 kann die Kartenfunktion reduziert werden auf: .map(r => r.source)

korun
quelle
3
Genau das, wonach ich gesucht habe, super sauber. Vielen Dank!
Marian Zagoruiko
9
Dies ist sehr praktisch, um einem langen regulären Ausdruck Kommentare hinzuzufügen. Es ist jedoch begrenzt, indem übereinstimmende Klammern in derselben Zeile stehen.
Nathan S. Watson-Haigh
Auf jeden Fall das! Super nett mit der Fähigkeit, jeden Sub-Regex zu kommentieren.
GaryO
Vielen Dank, es hat geholfen, die Quelle in die Regex-Funktion zu versetzen
Code
Sehr schlau. Danke, diese Idee hat mir sehr geholfen. Nur als Randnotiz: Ich habe das Ganze in einer Funktion zusammengefasst, um es noch sauberer zu machen: combineRegex = (...regex) => new RegExp(regex.map(r => r.source).join(""))Verwendung:combineRegex(/regex1/, /regex2/, ...)
Scindix
24

Das Verwenden von Strings in new RegExpist umständlich, da Sie alle Backslashes umgehen müssen. Sie können kleinere reguläre Ausdrücke schreiben und diese verketten.

Lassen Sie uns diesen regulären Ausdruck aufteilen

/^foo(.*)\bar$/

Wir werden eine Funktion verwenden, um die Dinge später schöner zu machen

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

Und jetzt lass uns rocken

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

Versuchen Sie, die echte Regex nur einmal zu erstellen, und verwenden Sie diese dann, da dies Kosten verursacht.

Riccardo Galli
quelle
6

Hier gibt es gute Antworten, aber der Vollständigkeit halber sollte jemand das Kernmerkmal von Javascript in Bezug auf die Vererbung mit der Prototypenkette erwähnen . So etwas veranschaulicht die Idee:

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

James Donohue
quelle
Dies ist die beste Antwort hier.
parttimeturtle
5

Dank der wunderbaren Welt der Vorlagenliterale können Sie jetzt große, mehrzeilige, gut kommentierte und sogar semantisch verschachtelte reguläre Ausdrücke in ES6 schreiben.

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

Auf diese Weise können Sie jetzt reguläre Ausdrücke wie folgt schreiben:

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

Ausgänge

/I'm a special regex{3}/

Oder was ist mit Multiline?

'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]

Ausgänge hel, ordentlich!
"Was ist, wenn ich tatsächlich eine neue Zeile suchen muss?", Dann benutze \ndumm!
Ich arbeite an meinem Firefox und Chrome.


Okay, "wie wäre es mit etwas etwas komplexerem?"
Sicher, hier ist ein Teil eines Objekts, das den JS-Parser zerstört, an dem ich gearbeitet habe :

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*[,}{]
    ).*)
$`

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

Und mit einer kleinen Demo?

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

Erfolgreich ausgegeben

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

Beachten Sie die erfolgreiche Erfassung der Zeichenfolge in Anführungszeichen.
Ich habe es auf Chrome und Firefox getestet, funktioniert ein Vergnügen!

Wenn Sie neugierig sind, können Sie sich ansehen, was ich getan habe und wie es demonstriert wird .
Dies funktioniert jedoch nur unter Chrome, da Firefox keine Rückreferenzen oder benannten Gruppen unterstützt. Beachten Sie also, dass das in dieser Antwort angegebene Beispiel tatsächlich eine kastrierte Version ist und möglicherweise leicht dazu verleitet wird, ungültige Zeichenfolgen zu akzeptieren.

Hashbrown
quelle
1
Sie sollten sich vorstellen, dies als NodeJS-Paket zu exportieren. Es ist wunderbar
rmobis
1
Obwohl ich es noch nie selbst gemacht habe, gibt es hier ein ziemlich gründliches Tutorial: zellwk.com/blog/publish-to-npm . Ich würde vorschlagen, np am Ende der Seite zu überprüfen. Ich habe es nie benutzt, aber Sindre Sorhus ist ein Zauberer mit diesen Dingen, also würde ich es nicht verpassen.
rmobis
4

In der obigen Regex fehlen einige schwarze Schrägstriche, die nicht richtig funktionieren. Also habe ich den regulären Ausdruck bearbeitet. Bitte beachten Sie diesen regulären Ausdruck, der für die E-Mail-Validierung zu 99,99% funktioniert.

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(''));
Anvesh Reddy
quelle
1

Um das Array zu vermeiden join, können Sie auch die folgende Syntax verwenden:

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,}))$');
andreasonny83
quelle
0

Persönlich würde ich mich für eine weniger komplizierte Regex entscheiden:

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

Sicher, es ist weniger genau als Ihr aktuelles Muster, aber was versuchen Sie zu erreichen? Versuchen Sie, versehentliche Fehler zu erfassen, die Ihre Benutzer möglicherweise eingeben, oder befürchten Sie, dass Ihre Benutzer versuchen, ungültige Adressen einzugeben? Wenn es das erste ist, würde ich mich für ein einfacheres Muster entscheiden. In letzterem Fall ist eine Überprüfung durch Beantwortung einer an diese Adresse gesendeten E-Mail möglicherweise die bessere Option.

Wenn Sie jedoch Ihr aktuelles Muster verwenden möchten, ist es (IMO) einfacher zu lesen (und zu warten!), Indem Sie es aus kleineren Untermustern wie folgt erstellen:

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 + ")$");
Bart Kiers
quelle
21
Downvoting - Obwohl Ihre Kommentare zur Reduzierung der Regex-Komplexität gültig sind, fragt OP speziell, wie "lange Regex über mehrere Zeilen aufgeteilt werden sollen". Obwohl Ihr Rat gültig ist, wurde er aus den falschen Gründen gegeben. z. B. Ändern der Geschäftslogik, um eine Programmiersprache zu umgehen. Darüber hinaus ist das von Ihnen angegebene Codebeispiel ziemlich hässlich.
Sleepycal
4
@sleepycal Ich denke, Bart hat die Frage beantwortet. Siehe den letzten Abschnitt seiner Antwort. Er hat die Frage beantwortet und eine Alternative gegeben.
Nidhin David
0

Sie können einfach die Zeichenfolgenoperation verwenden.

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);
Mubeena
quelle
0

Ich habe versucht, Koruns Antwort zu verbessern, indem ich alles gekapselt und Unterstützung für die Aufteilung von Erfassungsgruppen und Zeichensätzen implementiert habe - was diese Methode viel vielseitiger macht.

Um dieses Snippet zu verwenden, müssen Sie die Variadic-Funktion aufrufen, combineRegexderen Argumente die Objekte mit regulären Ausdrücken sind, die Sie kombinieren müssen. Die Implementierung finden Sie unten.

Das Erfassen von Gruppen kann jedoch nicht direkt auf diese Weise aufgeteilt werden, da einige Teile nur eine Klammer enthalten. Ihr Browser würde mit einer Ausnahme fehlschlagen.

Stattdessen übergebe ich einfach den Inhalt der Erfassungsgruppe innerhalb eines Arrays. Die Klammern werden automatisch hinzugefügt, wenn Sie combineRegexauf ein Array stoßen.

Außerdem müssen Quantifizierer etwas folgen. Wenn der reguläre Ausdruck aus irgendeinem Grund vor einem Quantifizierer aufgeteilt werden muss, müssen Sie ein Paar Klammern hinzufügen. Diese werden automatisch entfernt. Der Punkt ist, dass eine leere Erfassungsgruppe ziemlich nutzlos ist und Quantifizierer auf diese Weise etwas haben, auf das sie sich beziehen können. Die gleiche Methode kann für Dinge wie nicht erfassende Gruppen verwendet werden ( /(?:abc)/wird [/()?:abc/]).

Dies lässt sich am besten anhand eines einfachen Beispiels erklären:

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

würde werden:

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

Wenn Sie Zeichensätze teilen müssen, können Sie Objekte ( {"":[regex1, regex2, ...]}) anstelle von Arrays ( [regex1, regex2, ...]) verwenden. Der Inhalt des Schlüssels kann beliebig sein, solange das Objekt nur einen Schlüssel enthält. Beachten Sie, dass an Stelle von ()Ihnen verwenden ]als Dummy - Anfang , wenn das erste Zeichen als Quantifizierer interpretiert werden könnte. Dh /[+?]/wird{"":[/]+?/]}

Hier ist der Ausschnitt und ein vollständigeres Beispiel:

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

Scindix
quelle
0

@ Hashbrowns großartige Antwort hat mich auf den richtigen Weg gebracht. Hier ist meine Version, die ebenfalls von diesem Blog inspiriert wurde .

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

Verwenden Sie es so:

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*/
`

So erstellen Sie dieses RegExpObjekt:

/(\d+)([a-z]{1,3})/i
Nuno Cruces
quelle