B
An alternative is to use https://www.regular-expressions.info/lookaround.html :^(?=.*\b\d{6}\b)(?=.*\b\d{14}\b)
Each lookahead is delimited by (?= and ) and his idea is: lookahead search for the expression inside him, and if he finds, back to where he was and continues to evaluate the rest of the regex.In the case, the expression inside the first lookahead (?=.*\b\d{6}\b) means:.*: zero or more characters (any character)\b: http://php.net/manual/en/regexp.reference.escape.php ("word front"), which means that it is the position of the string that delimits a word (i.e. there is an alphanumeric character before and a non-alphanumeric after - or vice versa).\d{6}: exactly 6 digits\b: Other word-boundary. With this, we indicate that the 6 digits are delimited by non-alphanumeric characters (not at risk of catching sequences greater than 6 digits).This lookahead is right after https://www.regular-expressions.info/anchors.html ^, which means "start of string". That is, if lookahead find a 6-digit string somewhere in the string, it returns to where it was (in case, for the start of the string) and continues to evaluate the rest of the regex.In the case, the rest of the regex is another lookahead, very similar to the first, but looking for a sequence of exactly 14 digits.That is, this regex first checks if there is a 6-digit sequence. How \d{6} is in one lookahead, it returns to the beginning of the string and evaluates the second lookahead, looking for a 14-digit sequence.If any lookaheads fail, regex as a whole also fails. Example:$regex = '/^(?=.*\b\d{6}\b)(?=.*\b\d{14}\b)/';
echo preg_match($regex, "FORT/310117/200826/12979898000170") . "\n"; // 1
echo preg_match($regex, "FORT/310117/200826/") . "\n"; // 0
echo preg_match($regex, "FORT/12979898000170") . "\n"; // 0
http://php.net/manual/en/function.preg-match.php#refsect1-function.preg-match-returnvalues returns 1 if the regex finds a match and zero in case you don't. In the above case, it returned 1 only for the case where there are the two sequences (from 6 and 14 digits). For cases where there is only one of them, returns 0.This regex also does not accept 7-digit sequences, for example:// tem sequência de 14, mas não tem de 6 (somente de 7)
echo preg_match($regex, "FORT/3101176/12979898000170") . "\n"; // 0
If the delimiter is always /, you can change \b by the bar:$regex = '/^(?=./\d{6}(/|$))(?=./\d{14}(/|$))/';
The difference in this case is that the bar should be escaped and written as / (not to be confused with the regex delimiter at the beginning and end of the string). Besides, after the digit sequence I put (/|$), which means "a bar or the end of the string ($) \b that is not necessary, for \b already considers the end of the string as a word-boundary)."Split" instead of super-regex-que-do-allThe above regex only validates if the string has such digit sequences. But if you want to get the digits properly said, I find it easier to break the string and go through the parts one by one:$partes = explode("/", "FORT/310117/200826/12979898000170");
$temSequenciaDe6 = false;
$temSequenciaDe14 = false;
foreach ($partes as $parte) {
if (preg_match('/^\d{6}$/', $parte)) {
$temSequenciaDe6 = true;
echo $parte . "\n";
} else if (preg_match('/^\d{14}$/', $parte)) {
$temSequenciaDe14 = true;
echo $parte . "\n";
}
}
In this case the expressions are simpler. ^\d{6}$, for example, checks exactly 6 digits from the start (^) the end ($) string, while ^\d{14}$ check exactly 14 digits from start to finish.This code prints the numbers you want (only if you have 6 or 14 digits), and the Boolean values $temSequenciaDe6 and $temSequenciaDe14 indicate which ones exist in the string. To know if the string has both, just do:if ($temSequenciaDe6 && $temSequenciaDe14) {
// string possui sequências de 6 e de 14 dígitos
}
You can also break the string by using a regex as a criterion, in case / do not be the only tab. Just use http://php.net/manual/en/function.preg-split.php :$partes = preg_split('/[/ ]/', "FORT 310117 200826 12979898000170");
In this case, regex will be used to break the string. How I used it [/ ], that means so much to / how much space will be used as delimiters (repare that there is a space before ]).If the tabs are just one character, simply add all possible tabs within the brackets. For example, [/ ,-] considers that the separator can be the /, or a space, or the comma, or the hyphen. Place all the characters you need.If the tab has more than one character, then it is better to use https://www.regular-expressions.info/alternation.html (|). Ex: ( |/|xyz) would use a space, or the bar, or the string xyz as tab. If the tabs are just a character (not a string with 2 or more), I find it easier to use the brackets.Other alternativesI couldn't get a single regex that does both things (valid if there are 6 and 14 digit sequences and get the numbers). But if you don't want to use preg_split, you can use the first regex above (with 2 lookaheads) to validate the string, and then use the regex below to get the numbers:$str = "FORT/310117/200826/12979898000170";
if (preg_match('/^(?=.\b\d{6}\b)(?=.\b\d{14}\b)/', $str)) {
preg_match_all('/\b(\d{6}(\d{8})?)\b/', $str, $matches, PREG_SET_ORDER);
foreach($matches as $m) {
echo $m[0] . "\n";
}
}
The first regex (with the lookaheads) validates if the string contains at least one 6-digit sequence and a 14-digit. Then the second regex gets all the string numbers that match these sequences.The passage (\d{6}(\d{8})?) means "6 digits, followed or not 8 digits" - the ? after (\d{8}) makes this whole stretch optional. That is, this regex takes both 6 and 14 digits (and the \b before and after it ensures that it will not take extra digits). And like the previous regex (with the lookaheads) already assured that both sequences exist, I don't need to worry about checking it again. The output of this code is:310117
200826
12979898000170
One more optionAnother option is to use a regex to capture the 6-digit groups ( ensuring that there is at least a 14-digit group), and then another regex to do the reverse (capture the 14-digit groups, ensuring that there is at least one of 6).The first regex is:(?|(\b\d{6}\b)(?=.+?\b\d{14}\b)|(?<=\b\d{14}\b).+?(\b\d{6}\b))
The passage (\b\d{6}\b)(?=.+?\b\d{14}\b) means:(\b\d{6}\b): 6 digits (inside brackets for forming a catch group)(?=.+?\b\d{14}\b): lookahead to check for a 14-digit sequence aheadAnd the passage (?<=\b\d{14}\b).+?(\b\d{6}\b) means:(?<=\b\d{14}\b): lookbehind to check if there is a 14-digit sequence before the 6-digit sequence..+?: any characters. O + means "one or more" and ? means you will take the minimum of characters to satisfy the expression(\b\d{6}\b): 6 digitsThat is, the entire regex checks if there is a 14-digit sequence before or after the 6-digit sequence.I also use (?|, which means https://www.regular-expressions.info/branchreset.html . As the 6 digits appear twice in the expression, this means that it has 2 possible capture groups. If I don't use it reset branch, I will have to check which of the 2 groups is filled, but using it I guarantee that it will always be group 1.Then I can use the same logic to capture the 14-digit groups, and use the lookaheads and lookbehinds to check if there is at least a group of 6 digits before or after:$str = "FORT/310117/200826/12979898000170";
// pega os grupos de 6 dígitos (verificando se há grupo de 14 dígitos antes ou depois)
preg_match_all('/(?|(\b\d{6}\b)(?=.+?\b\d{14}\b)|(?<=\b\d{14}\b).+?(\b\d{6}\b))/', $str, $matches, PREG_SET_ORDER);
foreach($matches as $m) {
echo $m[1] . "\n";
}
// pega os grupos de 14 dígitos (verificando se há grupo de 6 dígitos antes ou depois)
preg_match_all('/(?|(\b\d{14}\b)(?=.+?\b\d{6}\b)|(?<=\b\d{6}\b).+?(\b\d{14}\b))/', $str, $matches, PREG_SET_ORDER);
foreach($matches as $m) {
echo $m[1] . "\n";
}
The first foreach take the numbers with 6 digits, and the second picks them with 14. The lookaheads and lookbehinds ensure that it will only catch one of the sequences if the other exists (it only takes 6 digits if there is at least one of 14, and vice versa).The output is:310117
200826
12979898000170
I tried to join the 2 regex above in one, but she ended up jumping the second number (200826), https://ideone.com/b7CxK0 . I haven't figured out the reason yet, but anyway, that was the closest thing I got to a single regex that takes all the numbers and validates if there is at least one sequence of 6 and another of 14 digits.