Как я могу сопоставить несколько вхождений с регулярным выражением в JavaScript, аналогичном PHP preg_match_all()?
Я пытаюсь разобрать строки с кодировкой url, которые состоят из пар ключ = значение, разделенных символом &
или &
.
Следующее будет соответствовать только первому вхождению, разбивая ключи и значения на отдельные элементы результата:
var result = mystring.match(/(?:&|&)?([^=]+)=([^&]+)/)
Результаты для строки '1111342 = Adam %20Franco & 348572 = Bob %20Jones' будут выглядеть следующим образом:
['1111342', 'Adam%20Franco']
Использование глобального флага, 'g', будет соответствовать всем вхождениям, но только возвращает полностью согласованные подстроки, а не разделенные ключи и значения:
var result = mystring.match(/(?:&|&)?([^=]+)=([^&]+)/g)
Результаты для строки '1111342 = Adam %20Franco & 348572 = Bob %20Jones' будут выглядеть следующим образом:
['1111342=Adam%20Franco', '&348572=Bob%20Jones']
В то время как я мог разделить строку на &
и разделить каждую пару ключ/значение отдельно, существует ли какой-либо способ поддержки регулярного выражения JavaScript для соответствия множественным вхождениям шаблона /(?:&|&)?([^=]+)=([^&]+)/
, аналогичному функции PHP preg_match_all()
?
Я пытаюсь каким-то образом получить результаты с разделителями, подобными следующим:
[['1111342', '348572'], ['Adam%20Franco', 'Bob%20Jones']]
или
[['1111342', 'Adam%20Franco'], ['348572', 'Bob%20Jones']]
Ответы
Ответ 1
Я бы предложил альтернативное регулярное выражение, используя подгруппы для индивидуального определения имени и значения параметров:
function getUrlParams(url) {
var re = /(?:\?|&(?:amp;)?)([^=&#]+)(?:=?([^&#]*))/g,
match, params = {},
decode = function (s) {return decodeURIComponent(s.replace(/\+/g, " "));};
if (typeof url == "undefined") url = document.location.href;
while (match = re.exec(url)) {
params[decode(match[1])] = decode(match[2]);
}
return params;
}
var result = getUrlParams("http://maps.google.de/maps?f=q&source=s_q&hl=de&geocode=&q=Frankfurt+am+Main&sll=50.106047,8.679886&sspn=0.370369,0.833588&ie=UTF8&ll=50.116616,8.680573&spn=0.35972,0.833588&z=11&iwloc=addr");
result
- это объект:
{
f: "q"
geocode: ""
hl: "de"
ie: "UTF8"
iwloc: "addr"
ll: "50.116616,8.680573"
q: "Frankfurt am Main"
sll: "50.106047,8.679886"
source: "s_q"
spn: "0.35972,0.833588"
sspn: "0.370369,0.833588"
z: "11"
}
Регулярное выражение разбивается следующим образом:
(?: # non-capturing group
\?|& # "?" or "&"
(?:amp;)? # (allow "&", for wrongly HTML-encoded URLs)
) # end non-capturing group
( # group 1
[^=]+ # any character except "=", "&" or "#"; at least once
) # end group 1 - this will be the parameter name
(?: # non-capturing group
=? # an "=", optional
( # group 2
[^]* # any character except "&" or "#"; any number of times
) # end group 2 - this will be the parameter value
) # end non-capturing group
Ответ 2
Для глобального поиска вам нужно использовать переключатель "g"
var result = mystring.match(/(&|&)?([^=]+)=([^&]+)/g)
Ответ 3
Если вы не хотите полагаться на "слепое сопоставление", которое происходит при выполнении сопоставления в стиле exec
, JavaScript действительно поставляется с встроенной функциональностью сопоставления всех, но он является частью вызова функции replace
, когда используется "что делать с Функция обработки групп захвата:
var data = {};
var getKeyValue = function(fullPattern, group1, group2, group3) {
data[group2] = group3;
};
mystring.replace(/(?:&|&)?([^=]+)=([^&]+)/g, getKeyValue);
сделанный.
Вместо того, чтобы использовать захват группу функции обработки фактически возвращают строки замены (для замены обработки, первый ARG, называется здесь, является полным совпадением картины, и последующие арги отдельных групп захвата, в этом случае a
b
будучи группу 1, c
группы 2 и т.д.) Мы просто берем захваты групп 2 и 3 и кешируем эту пару.
Таким образом, вместо того, чтобы писать сложные функции синтаксического анализа, помните, что функция "matchAll" в JavaScript просто "заменяет" на функцию обработчика замены, и можно добиться значительной эффективности сопоставления с образцом.
Ответ 4
Для захвата групп я использую preg_match_all
в PHP, и я попытался воспроизвести его здесь:
<script>
// Return all pattern matches with captured groups
RegExp.prototype.execAll = function(string) {
var match = null;
var matches = new Array();
while (match = this.exec(string)) {
var matchArray = [];
for (i in match) {
if (parseInt(i) == i) {
matchArray.push(match[i]);
}
}
matches.push(matchArray);
}
return matches;
}
// Example
var someTxt = 'abc123 def456 ghi890';
var results = /[a-z]+(\d+)/g.execAll(someTxt);
// Output
[["abc123", "123"],
["def456", "456"],
["ghi890", "890"]]
</script>
Ответ 5
Установите модификатор g
для глобального соответствия:
/…/g
Ответ 6
Источник:
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/RegExp/exec
Поиск последовательных матчей
Если ваше регулярное выражение использует флаг "g", вы можете использовать метод exec() несколько раз, чтобы найти последовательные совпадения в одной и той же строке. Когда вы это делаете, поиск начинается с подстроки str, указанной в свойстве регулярного выражения lastIndex (test() также опережает свойство lastIndex). Например, предположим, что у вас есть этот скрипт:
var myRe = /ab*/g;
var str = 'abbcdefabh';
var myArray;
while ((myArray = myRe.exec(str)) !== null) {
var msg = 'Found ' + myArray[0] + '. ';
msg += 'Next match starts at ' + myRe.lastIndex;
console.log(msg);
}
Этот скрипт отображает следующий текст:
Found abb. Next match starts at 3
Found ab. Next match starts at 912
Примечание. Не помещайте литерал регулярного выражения (или конструктор RegExp) в условие while, иначе он создаст бесконечный цикл при совпадении из-за того, что свойство lastIndex сбрасывается при каждой итерации. Также убедитесь, что установлен глобальный флаг, или здесь также произойдет цикл.
Ответ 7
Если кому-то (как и мне) нужен метод Томалака с поддержкой массива (т.е. множественный выбор), вот он:
function getUrlParams(url) {
var re = /(?:\?|&(?:amp;)?)([^=&#]+)(?:=?([^&#]*))/g,
match, params = {},
decode = function (s) {return decodeURIComponent(s.replace(/\+/g, " "));};
if (typeof url == "undefined") url = document.location.href;
while (match = re.exec(url)) {
if( params[decode(match[1])] ) {
if( typeof params[decode(match[1])] != 'object' ) {
params[decode(match[1])] = new Array( params[decode(match[1])], decode(match[2]) );
} else {
params[decode(match[1])].push(decode(match[2]));
}
}
else
params[decode(match[1])] = decode(match[2]);
}
return params;
}
var urlParams = getUrlParams(location.search);
ввод ?my=1&my=2&my=things
результат 1,2,things
(ранее возвращаемый только: вещи)
Ответ 8
Чтобы придерживаться предложенного вопроса, как указано в названии, вы можете фактически перебирать каждое соответствие в строке с помощью String.prototype.replace()
. Например, следующее делает именно это, чтобы получить массив всех слов на основе регулярного выражения:
function getWords(str) {
var arr = [];
str.replace(/\w+/g, function(m) {
arr.push(m);
});
return arr;
}
var words = getWords("Where in the world is Carmen Sandiego?");
// > ["Where", "in", "the", "world", "is", "Carmen", "Sandiego"]
Если бы я хотел получить группы захвата или даже индекс каждого совпадения, я тоже мог бы это сделать. Ниже показано, как возвращаются каждое совпадение со всем совпадением, первая группа захвата и индекс:
function getWords(str) {
var arr = [];
str.replace(/\w+(?=(.*))/g, function(m, remaining, index) {
arr.push({ match: m, remainder: remaining, index: index });
});
return arr;
}
var words = getWords("Where in the world is Carmen Sandiego?");
После выполнения выше, words
будет выглядеть следующим образом:
[
{
"match": "Where",
"remainder": " in the world is Carmen Sandiego?",
"index": 0
},
{
"match": "in",
"remainder": " the world is Carmen Sandiego?",
"index": 6
},
{
"match": "the",
"remainder": " world is Carmen Sandiego?",
"index": 9
},
{
"match": "world",
"remainder": " is Carmen Sandiego?",
"index": 13
},
{
"match": "is",
"remainder": " Carmen Sandiego?",
"index": 19
},
{
"match": "Carmen",
"remainder": " Sandiego?",
"index": 22
},
{
"match": "Sandiego",
"remainder": "?",
"index": 29
}
]
Чтобы сопоставить множественные вхождения, похожие на то, что доступно на PHP, preg_match_all
, вы можете использовать этот тип мышления, чтобы сделать свой собственный или используйте что-то вроде YourJS.matchAll()
. Функция более подробно описана ниже:
function matchAll(str, rgx) {
var arr, extras, matches = [];
str.replace(rgx.global ? rgx : new RegExp(rgx.source, (rgx + '').replace(/[\s\S]+\//g , 'g')), function() {
matches.push(arr = [].slice.call(arguments));
extras = arr.splice(-2);
arr.index = extras[0];
arr.input = extras[1];
});
return matches[0] ? matches : null;
}
Ответ 9
Если вы можете избежать использования map
, это четырехстрочное решение:
var mystring = '1111342=Adam%20Franco&348572=Bob%20Jones';
var result = mystring.match(/(&|&)?([^=]+)=([^&]+)/g) || [];
result = result.map(function(i) {
return i.match(/(&|&)?([^=]+)=([^&]+)/);
});
console.log(result);
Ответ 10
Используйте window.URL
:
> s = 'http://www.example.com/index.html?1111342=Adam%20Franco&348572=Bob%20Jones'
> u = new URL(s)
> Array.from(u.searchParams.entries())
[["1111342", "Adam Franco"], ["348572", "Bob Jones"]]
Ответ 11
Ну... У меня была аналогичная проблема...
Я хочу инкрементный/шаг поиска с RegExp
(например: начать поиск... выполнить некоторую обработку... продолжить поиск до последнего совпадения)
После много интернет-поиска... как всегда (теперь это привычка)
Я попал в StackOverflow и нашел ответ...
Что не упоминается и имеет значение, это "lastIndex
"
Теперь я понимаю, почему объект RegExp реализует свойство <lastIndex
Ответ 12
Чтобы захватить несколько параметров с использованием одного и того же имени, я изменил цикл while в методе Tomalak следующим образом:
while (match = re.exec(url)) {
var pName = decode(match[1]);
var pValue = decode(match[2]);
params[pName] ? params[pName].push(pValue) : params[pName] = [pValue];
}
input: ?firstname=george&lastname=bush&firstname=bill&lastname=clinton
возвращает: {firstname : ["george", "bill"], lastname : ["bush", "clinton"]}
Ответ 13
Разделение выглядит как лучший вариант для меня:
'1111342=Adam%20Franco&348572=Bob%20Jones'.split('&').map(x => x.match(/(?:&|&)?([^=]+)=([^&]+)/))
Ответ 14
Чтобы избежать регулярного выражения ада, вы можете найти свой первый матч, отрубите кусок и попытайтесь найти следующий в подстроке. В С# это выглядит примерно так, извините, я не перенес это на JavaScript для вас.
long count = 0;
var remainder = data;
Match match = null;
do
{
match = _rgx.Match(remainder);
if (match.Success)
{
count++;
remainder = remainder.Substring(match.Index + 1, remainder.Length - (match.Index+1));
}
} while (match.Success);
return count;