Làm cách nào tôi có thể ghép nối các chữ regex trong JavaScript?


145

Có thể làm một cái gì đó như thế này?

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

Hay tôi phải sử dụng RegExp()cú pháp mới và nối chuỗi? Tôi muốn sử dụng nghĩa đen vì mã vừa rõ ràng và ngắn gọn hơn.


1
Việc xử lý các ký tự regex thoát được dễ dàng hơn nếu bạn sử dụng String.raw ():let regexSegment1 = String.raw`\s*hello\s*`
iono

Câu trả lời:


190

Dưới đây là cách tạo biểu thức chính quy mà không cần sử dụng cú pháp biểu thức chính quy. Điều này cho phép bạn thực hiện thao tác chuỗi tùy ý trước khi nó trở thành một đối tượng biểu thức chính quy:

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

Nếu bạn có hai biểu thức chính quy, trên thực tế bạn có thể ghép chúng bằng kỹ thuật này:

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

Nó chỉ dài dòng hơn là chỉ có một và hai là các chuỗi theo nghĩa đen thay vì các biểu thức thông thường theo nghĩa đen.


1
Hãy nhớ rằng mỗi phân đoạn phải là một biểu thức chính quy hợp lệ khi sử dụng phương pháp này. Xây dựng một biểu thức như new RegExp(/(/.source + /.*/.source + /)?/.source);dường như không hoạt động.
Sam

Giải pháp này không hoạt động trong trường hợp các nhóm khớp lại. Xem câu trả lời của tôi cho một giải pháp làm việc trong trường hợp đó.
Mikaël Mayer

Nếu bạn cần thoát một char, sau đó sử dụng dấu gạch chéo kép: Regapi mới ('\\ $' + "flum")
Jeff Lowery

Bạn có thể truy cập các cờ nếu bạn phải sử dụng "<regapi> .flags", vì vậy về mặt lý thuyết bạn cũng có thể kết hợp chúng.
bnunamak

Bạn đến expression_onetừ đâu Ý bạn là regex1sao
TallOrderDev

30

Chỉ cần ngẫu nhiên ghép các đối tượng biểu thức chính quy có thể có một số tác dụng phụ bất lợi. Sử dụng RegExp.source thay thế:

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

Điều này cũng sẽ cung cấp cho bạn khả năng giữ lại các cờ biểu thức chính quy từ RegExp trước đó bằng các cờ RegExp tiêu chuẩn.

jsFiddle


Điều này có thể được cải thiện bằng cách sử dụngRegExp.prototype.flags
Dmitry Parzhitsky

19

Tôi không hoàn toàn đồng ý với tùy chọn "eval".

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

sẽ cho "// abcd // efgh //" không phải là kết quả dự định.

Sử dụng nguồn như

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

sẽ cho "/ abcdefgh /" và điều đó là chính xác.

Logic không cần phải ĐÁNH GIÁ, bạn biết R EX RÀNG của bạn. Bạn chỉ cần NGUỒN của nó hoặc làm thế nào nó được viết không nhất thiết là giá trị của nó. Đối với các cờ, bạn chỉ cần sử dụng đối số tùy chọn của RegExp.

Trong tình huống của tôi, tôi chạy trong vấn đề ^ và $ được sử dụng trong một số biểu thức tôi đang cố gắng kết hợp với nhau! Những biểu thức đó là các bộ lọc ngữ pháp được sử dụng trên chương trình. Bây giờ tôi sẽ không sử dụng một số trong số họ với nhau để xử lý trường hợp TRƯỚC. Tôi có thể phải "cắt" các nguồn để xóa phần bắt đầu và kết thúc ^ (và / hoặc) $ :) Chúc mừng, Alex.


Tôi thích việc sử dụng tài sản nguồn. Nếu bạn - như tôi - sử dụng jslint, nó sẽ cằn nhằn nếu bạn làm điều gì đó như thế này:var regex = "\.\..*"
Nils-o-mat

7

Sự cố Nếu biểu thức chính quy có chứa các nhóm khớp lại như \ 1.

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

Sau đó, chỉ cần liên kết các nguồn sẽ không hoạt động. Thật vậy, sự kết hợp của hai là:

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

Giải pháp: Đầu tiên chúng tôi đếm số lượng nhóm phù hợp trong regex đầu tiên, sau đó với mỗi mã thông báo khớp ngược lại trong lần thứ hai, chúng tôi sẽ tăng nó theo số lượng nhóm phù hợp.

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' : ''));
}

Kiểm tra:

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

2
Có (tôi sẽ không sửa đổi nó ở đây). Hàm này có tính kết hợp, vì vậy bạn có thể sử dụng mã sau: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

Nó là tốt hơn để sử dụng cú pháp bằng chữ thường xuyên nhất có thể. Nó ngắn hơn, dễ đọc hơn và bạn không cần báo giá thoát hoặc thoát ngược lại. Từ "Mô hình Javascript", Stoyan Stefanov 2010.

Nhưng sử dụng Mới có thể là cách duy nhất để nối.

Tôi sẽ tránh eval. Nó không an toàn.


1
Tôi nghĩ rằng các biểu thức chính quy phức tạp dễ đọc hơn khi chia tay và nhận xét như trong câu hỏi.
Sam

3

Cung cấp rằng:

  • bạn biết những gì bạn làm trong regrec của bạn;
  • bạn có nhiều mảnh regex để tạo thành một mẫu và chúng sẽ sử dụng cùng một cờ;
  • bạn thấy dễ đọc hơn khi tách các phần mẫu nhỏ của bạn thành một mảng;
  • bạn cũng muốn có thể nhận xét từng phần cho nhà phát triển tiếp theo hoặc chính bạn sau này;
  • bạn thích đơn giản hóa trực quan regex của bạn /this/ghơn là new RegExp('this', 'g');
  • Bạn có thể lắp ráp regex trong một bước bổ sung thay vì bắt đầu từ một mảnh ngay từ đầu;

Sau đó, bạn có thể muốn viết theo cách này:

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

sau đó bạn có thể làm một cái gì đó như:

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

Trong trường hợp cụ thể của tôi (một trình soạn thảo giống như mã nhân bản), việc thực hiện một regex lớn sẽ dễ dàng hơn nhiều, thay vì nhiều thay thế như sau mỗi lần tôi thay thế bằng thẻ html để bọc biểu thức, mẫu tiếp theo sẽ khó khăn hơn để mục tiêu mà không ảnh hưởng đến các thẻ html bản thân (và không có lợi ích lookbehind được tiếc là không được hỗ trợ trong javascript):

.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

Bạn có thể làm một cái gì đó như:

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

Các phân đoạn sẽ là các chuỗi (chứ không phải là regex bằng chữ) được truyền vào dưới dạng các đối số riêng biệt.


1

Không, cách hiểu theo nghĩa đen không được hỗ trợ. Bạn sẽ phải sử dụng RegExp.


1

Sử dụng hàm tạo với 2 thông số và tránh sự cố với dấu '/':

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

Bạn có thể nối nguồn regex từ cả hai lớp theo nghĩa đen và RegExp:

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

1

cách dễ dàng hơn với tôi sẽ là nối các nguồn, ví dụ:

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

giá trị c sẽ dẫn đến:

/ \ d + \ w + /


-2

Tôi thích sử dụng eval('your expression')bởi vì nó không thêm /vào mỗi đầu /='new RegExp'không.

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.