Intereting Posts
Как безопасно поддерживать аутентификацию пользователя через сторонний API? PHP или Linux Shell: самый быстрый способ уменьшить текстовый файл с более чем 10 строками, чтобы иметь только последние 10 строк MySql – Как выбрать диапазон идентификаторов с установленными интервалами Laravel 4 удаление нескольких моделей, связанных с внешними ключами API-интерфейс Analytics – Получить данные MCF = пустые результаты Как отслеживать использование пользователей на сайте? Nginx и PHP-cgi – не могут file_get_contents любого веб-сайта на сервере очистка переменных $ _POST Как написать конкретный CSS для mozilla, chrome и IE PHP автоматически убивает скрипт, если HTTP-запрос отменен / закрыт make html table <thead> исправлено при прокрутке в динамическом <tbody> в php Работа с двумя менеджерами сущностей в том же пакете в Symfony2 Анонимные функции не работают: имя функции должно быть строкой Как я могу проверить знак плюса с помощью регулярных выражений? php class extends, переменная из другого расширенного класса

Функция создания регулярных выражений, соответствующих диапазону чисел

Я работаю с API Amazon Mechanical Turk API, и это позволит мне использовать регулярные выражения для фильтрации поля данных.

Я хотел бы ввести целочисленный диапазон для функции, например 256-311 или 45-1233, и вернуть регулярное выражение, которое будет соответствовать только этому диапазону.

Соответствующим регулярным выражением 256-321 будет:

\b((25[6-9])|(2[6-9][0-9])|(3[0-1][0-9])|(32[0-1]))\b 

Эта часть довольно проста, но у меня возникают проблемы с циклом для создания этого регулярного выражения.

Я пытаюсь построить функцию, определенную следующим образом:

 function getRangeRegex( int fromInt, int toInt) { return regexString; } 

Я посмотрел по сети, и я удивлен, что это не похоже, что кто-то решил это в прошлом. Это сложная проблема …

Спасибо за ваше время.

Вот быстрый взлом:

 <?php function regex_range($from, $to) { if($from < 0 || $to < 0) { throw new Exception("Negative values not supported"); } if($from > $to) { throw new Exception("Invalid range $from..$to, from > to"); } $ranges = array($from); $increment = 1; $next = $from; $higher = true; while(true) { $next += $increment; if($next + $increment > $to) { if($next <= $to) { $ranges[] = $next; } $increment /= 10; $higher = false; } else if($next % ($increment*10) === 0) { $ranges[] = $next; $increment = $higher ? $increment*10 : $increment/10; } if(!$higher && $increment < 10) { break; } } $ranges[] = $to + 1; $regex = '/^(?:'; for($i = 0; $i < sizeof($ranges) - 1; $i++) { $str_from = (string)($ranges[$i]); $str_to = (string)($ranges[$i + 1] - 1); for($j = 0; $j < strlen($str_from); $j++) { if($str_from[$j] == $str_to[$j]) { $regex .= $str_from[$j]; } else { $regex .= "[" . $str_from[$j] . "-" . $str_to[$j] . "]"; } } $regex .= "|"; } return substr($regex, 0, strlen($regex)-1) . ')$/'; } function test($from, $to) { try { printf("%-10s %s\n", $from . '-' . $to, regex_range($from, $to)); } catch (Exception $e) { echo $e->getMessage() . "\n"; } } test(2, 8); test(5, 35); test(5, 100); test(12, 1234); test(123, 123); test(256, 321); test(256, 257); test(180, 195); test(2,1); test(-2,4); ?> 

который производит:

 2-8 /^(?:[2-7]|8)$/ 5-35 /^(?:[5-9]|[1-2][0-9]|3[0-5])$/ 5-100 /^(?:[5-9]|[1-9][0-9]|100)$/ 12-1234 /^(?:1[2-9]|[2-9][0-9]|[1-9][0-9][0-9]|1[0-2][0-3][0-4])$/ 123-123 /^(?:123)$/ 256-321 /^(?:25[6-9]|2[6-9][0-9]|3[0-2][0-1])$/ 256-257 /^(?:256|257)$/ 180-195 /^(?:18[0-9]|19[0-5])$/ Invalid range 2..1, from > to Negative values not supported 

Не надлежащим образом проверенные, используйте на свой страх и риск!

И да, сгенерированное регулярное выражение может быть написано более компактным во многих случаях, но я оставляю это как упражнение для читателя 🙂

Для тех, кто, как и я, искал версию превосходного @Bart Kiers's выше

 //Credit: Bart Kiers 2011 function regex_range(from, to){ if(from < 0 || to < 0) { //throw new Exception("Negative values not supported"); return null; } if(from > to) { //throw new Exception("Invalid range from..to, from > to"); return null; } var ranges = []; ranges.push(from); var increment = 1; var next = from; var higher = true; while(true){ next += increment; if(next + increment > to) { if(next <= to) { ranges.push(next); } increment /= 10; higher = false; }else{ if(next % (increment*10) == 0) { ranges.push(next); increment = higher ? increment*10 : increment/10; } } if(!higher && increment < 10) { break; } } ranges.push(to + 1); var regex = '/^(?:'; for(var i = 0; i < ranges.length - 1; i++) { var str_from = ranges[i]; str_from = str_from.toString(); var str_to = ranges[i + 1] - 1; str_to = str_to.toString(); for(var j = 0; j < str_from.length; j++) { if(str_from[j] == str_to[j]) { regex += str_from[j]; } else { regex += "[" + str_from[j] + "-" + str_to[j] + "]"; } } regex += "|"; } return regex.substr(0, regex.length - 1 ) + ')$/'; } в //Credit: Bart Kiers 2011 function regex_range(from, to){ if(from < 0 || to < 0) { //throw new Exception("Negative values not supported"); return null; } if(from > to) { //throw new Exception("Invalid range from..to, from > to"); return null; } var ranges = []; ranges.push(from); var increment = 1; var next = from; var higher = true; while(true){ next += increment; if(next + increment > to) { if(next <= to) { ranges.push(next); } increment /= 10; higher = false; }else{ if(next % (increment*10) == 0) { ranges.push(next); increment = higher ? increment*10 : increment/10; } } if(!higher && increment < 10) { break; } } ranges.push(to + 1); var regex = '/^(?:'; for(var i = 0; i < ranges.length - 1; i++) { var str_from = ranges[i]; str_from = str_from.toString(); var str_to = ranges[i + 1] - 1; str_to = str_to.toString(); for(var j = 0; j < str_from.length; j++) { if(str_from[j] == str_to[j]) { regex += str_from[j]; } else { regex += "[" + str_from[j] + "-" + str_to[j] + "]"; } } regex += "|"; } return regex.substr(0, regex.length - 1 ) + ')$/'; } 

Есть ли причина, по которой это должно быть регулярное выражение? не может сделать что-то вроде этого:

 if ($number >= 256 && $number <= 321){ // do something } 

Обновить:

Существует простой, но уродливый способ сделать это, используя диапазон :

 function getRangeRegex($from, $to) { $range = implode('|', range($from, $to)); // returns: 256|257|...|321 return $range; } 

Это уже сделано уже.

Посмотрите на этот сайт. Он содержит ссылку на скрипт python, который автоматически генерирует эти регулярные выражения для вас.

Этот ответ дублируется из этого вопроса . Я также сделал это в блоге


Использование регулярных выражений для проверки числового диапазона

Чтобы быть ясным: когда достаточно простого утверждения if

 if(num < -2055 || num > 2055) { throw new IllegalArgumentException("num (" + num + ") must be between -2055 and 2055"); } 

использование регулярных выражений для проверки числовых диапазонов не рекомендуется.

Кроме того, поскольку регулярные выражения анализируют строки, числа сначала должны быть переведены в строку до того, как они могут быть протестированы (исключение – это когда число уже является строкой, например, при получении пользовательского ввода с консоли).

(Чтобы гарантировать, что строка начинается с номера, вы можете использовать org.apache.commons.lang3.math. NumberUtils # isNumber(s) )

Несмотря на это, выяснение того, как проверять диапазоны чисел с регулярными выражениями, интересно и поучительно.

Один диапазон номеров

Правило: число должно быть ровно 15 .

Самый простой диапазон. Регулярное выражение соответствует этому

 \b15\b 

Границы слова необходимы, чтобы избежать совпадения 15 внутри 8215242 .

Диапазон двух чисел

Правило: число должно быть от 15 до 16 . Три возможных регулярных выражения:

 \b(15|16)\b \b1(5|6)\b \b1[5-6]\b 

Диапазон чисел «зеркальный» вокруг нуля

Правило: число должно быть от -12 до 12 .

Вот регулярное выражение от 0 до 12 , только положительное:

 \b(\d|1[0-2])\b 

Свободный интервал:

 \b( //The beginning of a word (or number), followed by either \d // Any digit 0 through 9 | //Or 1[0-2] // A 1 followed by any digit between 0 and 2. )\b //The end of a word 

Выполнение этой работы как для отрицательной, так и для позитивной так же просто, как добавление необязательной тире в начале:

 -?\b(\d|1[0-2])\b 

(Это предполагает, что никакие неуместные символы не предшествуют тире.)

Чтобы запретить отрицательные числа, необходим негативный lookbehind:

 (?<!-)\b(\d|1[0-2])\b 

Если вы оставите внешний вид, это приведет к тому, что 11 в -11 будут соответствовать. (Первый пример в этом сообщении должен содержать это.)

Примечание: \d против [0-9]

Чтобы быть совместимым со всеми ароматами регулярных выражений, все \d -s следует изменить на [0-9] . Например, .NET рассматривает не ASCII-номера, например, на разных языках, как законные значения для \d . За исключением последнего примера, для краткости он остается как \d .

(С благодарностью TimPietzcker в stackoverflow )

Три цифры, причем все, кроме первой цифры, равны нулю

Правило: должно быть от 0 до 400 .

Возможное регулярное выражение:

 (?<!-)\b([1-3]?\d{1,2}|400)\b 

Свободные интервалы:

  (?<!-) //Something not preceded by a dash \b( //Word-start, followed by either [1-3]? // No digit, or the digit 1, 2, or 3 \d{1,2} // Followed by one or two digits (between 0 and 9) | //Or 400 // The number 400 )\b //Word-end 

Другая возможность, которая никогда не должна использоваться :

 \b(0|1|2|3|4|5|6|7|8|9|10|11|12|13|14|15|16|17|18|19|20|21|22|23|24|25|26|27|28|29|30|31|32|33|34|35|36|37|38|39|40|41|42|43|44|45|46|47|48|49|50|51|52|53|54|55|56|57|58|59|60|61|62|63|64|65|66|67|68|69|70|71|72|73|74|75|76|77|78|79|80|81|82|83|84|85|86|87|88|89|90|91|92|93|94|95|96|97|98|99|100|101|102|103|104|105|106|107|108|109|110|111|112|113|114|115|116|117|118|119|120|121|122|123|124|125|126|127|128|129|130|131|132|133|134|135|136|137|138|139|140|141|142|143|144|145|146|147|148|149|150|151|152|153|154|155|156|157|158|159|160|161|162|163|164|165|166|167|168|169|170|171|172|173|174|175|176|177|178|179|180|181|182|183|184|185|186|187|188|189|190|191|192|193|194|195|196|197|198|199|200|201|202|203|204|205|206|207|208|209|210|211|212|213|214|215|216|217|218|219|220|221|222|223|224|225|226|227|228|229|230|231|232|233|234|235|236|237|238|239|240|241|242|243|244|245|246|247|248|249|250|251|252|253|254|255|256|257|258|259|260|261|262|263|264|265|266|267|268|269|270|271|272|273|274|275|276|277|278|279|280|281|282|283|284|285|286|287|288|289|290|291|292|293|294|295|296|297|298|299|300|301|302|303|304|305|306|307|308|309|310|311|312|313|314|315|316|317|318|319|320|321|322|323|324|325|326|327|328|329|330|331|332|333|334|335|336|337|338|339|340|341|342|343|344|345|346|347|348|349|350|351|352|353|354|355|356|357|358|359|360|361|362|363|364|365|366|367|368|369|370|371|372|373|374|375|376|377|378|379|380|381|382|383|384|385|386|387|388|389|390|391|392|393|394|395|396|397|398|399|400)\b 

Окончательный пример: четыре цифры, отраженные вокруг нуля, которые не заканчиваются нулями.

Правило: должно быть от -2055 до 2055

Это из вопроса о stackoverflow.

Regex:

 -?\b(20(5[0-5]|[0-4][0-9])|1?[0-9]{1,3})\b 

Свободный интервал:

  -? //Optional dash \b( //Followed by word boundary, followed by either of the following 20( // "20", followed by either 5[0-5] // A "5" followed by a digit 0-5 | // or [0-4][0-9] // A digit 0-4, followed by any digit ) | //OR 1?[0-9]{1,3} // An optional "1", followed by one through three digits (0-9) )\b //Followed by a word boundary. 

Вот визуальное представление этого регулярного выражения:

И здесь вы можете попробовать сами: демонстрация Debuggex

(С благодарностью PlasmaPower от stackoverflow для поддержки отладки).

Итоговая записка

В зависимости от того, что вы захватываете , вполне вероятно, что все подгруппы должны быть сделаны в группы без захвата. Например, это:

 (-?\b(?:20(?:5[0-5]|[0-4][0-9])|1?[0-9]{1,3})\b) 

Вместо этого:

 -?\b(20(5[0-5]|[0-4][0-9])|1?[0-9]{1,3})\b 

Пример реализации Java

  import java.util.Scanner; import java.util.regex.Matcher; import java.util.regex.Pattern; import org.apache.commons.lang.math.NumberUtils; /** <P>Confirm a user-input number is a valid number by reading a string an testing it is numeric before converting it to an it--this loops until a valid number is provided.</P> <P>{@code java UserInputNumInRangeWRegex}</P> **/ public class UserInputNumInRangeWRegex { public static final void main(String[] ignored) { int num = -1; boolean isNum = false; int iRangeMax = 2055; //"": Dummy string, to reuse matcher Matcher mtchrNumNegThrPos = Pattern.compile("-?\\b(20(5[0-5]|[0-4][0-9])|1?[0-9]{1,3})\\b").matcher(""); do { System.out.print("Enter a number between -" + iRangeMax + " and " + iRangeMax + ": "); String strInput = (new Scanner(System.in)).next(); if(!NumberUtils.isNumber(strInput)) { System.out.println("Not a number. Try again."); } else if(!mtchrNumNegThrPos.reset(strInput).matches()) { System.out.println("Not in range. Try again."); } else { //Safe to convert num = Integer.parseInt(strInput); isNum = true; } } while(!isNum); System.out.println("Number: " + num); } } 

Вывод

 [C:\java_code\]java UserInputNumInRangeWRegex Enter a number between -2055 and 2055: tuhet Not a number. Try again. Enter a number between -2055 and 2055: 283837483 Not in range. Try again. Enter a number between -2055 and 2055: -200000 Not in range. Try again. Enter a number between -2055 and 2055: -300 Number: -300 

Я преобразовал ответ Барта Кирса в C ++. Функция принимает два целых числа в качестве входных данных и генерирует регулярное выражение для диапазона чисел.

 #include <stdio.h> #include <iostream> #include <vector> #include <string> std::string regex_range(int from, int to); int main(int argc, char **argv) { std::string regex = regex_range(1,100); std::cout << regex << std::endl; return 0; } std::string regex_range(int from, int to) //Credit: Bart Kiers 2011 { if(from < 0 || to < 0) { std::cout << "Negative values not supported. Exiting." << std::endl; return 0; } if(from > to) { std::cout << "Invalid range, from > to. Exiting." << std::endl; return 0; } std::vector<int> ranges; ranges.push_back(from); int increment = 1; int next = from; bool higher = true; while(true) { next += increment; if(next + increment > to) { if(next <= to) { ranges.push_back(next); } increment /= 10; higher = false; } else if(next % (increment*10) == 0) { ranges.push_back(next); increment = higher ? increment*10 : increment/10; } if(!higher && (increment < 10)) { break; } } ranges.push_back(to + 1); std::string regex("^(?:"); for(int i = 0; i < ranges.size() - 1; i++) { int current_from = ranges.at(i); std::string str_from = std::to_string(current_from); int current_to = ranges.at(i + 1) - 1; std::string str_to = std::to_string(current_to); for(int j = 0; j < str_from.length(); j++) { if(str_from.at(j) == str_to.at(j)) { std::string str_from_at_j(&str_from.at(j)); regex.append(str_from_at_j); } else { std::string str_from_at_j(&str_from.at(j)); std::string str_to_at_j(&str_to.at(j)); regex.append("["); regex.append(str_from_at_j); regex.append("-"); regex.append(str_to_at_j); regex.append("]"); } } regex.append("|"); } regex = regex.substr(0, regex.length() - 1); regex.append(")$"); return regex; } в #include <stdio.h> #include <iostream> #include <vector> #include <string> std::string regex_range(int from, int to); int main(int argc, char **argv) { std::string regex = regex_range(1,100); std::cout << regex << std::endl; return 0; } std::string regex_range(int from, int to) //Credit: Bart Kiers 2011 { if(from < 0 || to < 0) { std::cout << "Negative values not supported. Exiting." << std::endl; return 0; } if(from > to) { std::cout << "Invalid range, from > to. Exiting." << std::endl; return 0; } std::vector<int> ranges; ranges.push_back(from); int increment = 1; int next = from; bool higher = true; while(true) { next += increment; if(next + increment > to) { if(next <= to) { ranges.push_back(next); } increment /= 10; higher = false; } else if(next % (increment*10) == 0) { ranges.push_back(next); increment = higher ? increment*10 : increment/10; } if(!higher && (increment < 10)) { break; } } ranges.push_back(to + 1); std::string regex("^(?:"); for(int i = 0; i < ranges.size() - 1; i++) { int current_from = ranges.at(i); std::string str_from = std::to_string(current_from); int current_to = ranges.at(i + 1) - 1; std::string str_to = std::to_string(current_to); for(int j = 0; j < str_from.length(); j++) { if(str_from.at(j) == str_to.at(j)) { std::string str_from_at_j(&str_from.at(j)); regex.append(str_from_at_j); } else { std::string str_from_at_j(&str_from.at(j)); std::string str_to_at_j(&str_to.at(j)); regex.append("["); regex.append(str_from_at_j); regex.append("-"); regex.append(str_to_at_j); regex.append("]"); } } regex.append("|"); } regex = regex.substr(0, regex.length() - 1); regex.append(")$"); return regex; }