I am trying to create a serial number checker.
Serial Numbers are in ranges
A87594 - A92778
AB34534 - AC23405
B23933 - C344444
I was able to get the numbers to work using range() for the first serial number example, I'm guessing I need to use explode() but I wasn't sure how to explode the letters into a variable and the numbers into a seperate variable.
if($_POST['submit']) {
$snum = $_POST['serial_number'];
// 1952
$jan01_jan07 = range(87594, 92478);
if (in_array($snum, $jan01_jan07)) {
echo 'You have a 1952 Widget';
}
else {
echo 'Your serial number is unknown';
}
}
You can try using strcmp, as it checks two strings, so you can check whether the incoming data is equal to or more than the lower bound and less than or equal to the upper bound, like this:
$data = $_POST['data']; // change this accordingly
if(strcmp($data, $lowerBound) >= 0 && strcmp($data, $upperBound) <= 0) {
// successful match
}
As strcmp returns -1, 0, 1 if $data is before, the same as and after $lowerBound (dictionary ordered), so this works for strings as well.
Try something along these lines:
preg_match('/([A-C]+)(\d+)/', $serial, $matches);
list(, $characters, $numbers) = $matches;
From there is kind of depends on the exact rules that govern your serials, something along these lines should do:
if ($characters == 'A' && 87594 <= $numbers && $numbers <= 92778) {
return true;
} else if ($characters == 'AB' …) ...
Related
What is the way to search the database (mysql/php code) for the following entries:
123XX
123XY
XYZ44
1X344
1Z344
Z23YY
The input letters are only X - Y - Z and the numbers from 0 to 9
They are all one number, which is (12344), so how can I show these results? The goal is to search for repeated entries.
Another example :
12XYY
X = 3,4,5,6,7,8,9,0
Y = 3,4,5,6,7,8,9,0
Provided that y is not equal to x or any apparent number (1,2)
And X is not equal to Y or any apparent number (1,2)
$number = "1XZYY";
$rnumber = str_replace(array('Y','X','Z'), "ـ", $number);
$lenNumber = strlen(5);
$duplicate = $mysqli->query("SELECT `number` FROM `listNumber` WHERE (length(`number`) = '$lenNumber' && `number` LIKE '%$rnumber%') OR (length(`number`) = '$lenNumber' && `number`LIKE '%$rnumber%')");
I tried many methods, but it was very slow in showing the results because I put the loop inside a loop to search for every number in the first loop
I understand you want to look for 12344, but some of the digits may be been redacted and replaced with a random capital letter in XYZ. For that, you can use a regular expression:
WHERE REGEXP_LIKE(foo, '^[XYZ1][XYZ2][XYZ3][XYZ4][XYZ4]$')
Demo
I would use PHP and occupy each digit into the correct position until I find a conflict. To prevent a double loop I use a dictionary helper object to hold values of X, Y and Z.
function match_str_to_number($str, $number)
{
if (strlen($number) != strlen($str)) {
return false;
}
$dict = ['X' => -1, 'Y' => -1, 'Z' => -1];
for ($i = 0; $i < strlen($number); $i++) {
if ($number[$i] != $str[$i]) {
// a number mismatch
if (!isset($dict[$str[$i]])) {
return false;
}
// a wildcard variable conflict
if ($dict[$str[$i]] != $number[$i] && $dict[$str[$i]] != -1) {
return false;
};
$dict[$str[$i]] = $number[$i];
}
}
return true;
}
echo match_str_to_number("XYZ44", "12344") ? "true" : "false";
echo match_str_to_number("XYZ4X", "12344") ? "true" : "false";
// output: truefalse
In Maria DB table I have two varbinary(16) fields that represent IPv6 addresses for the start and end of the IPv6 range.
I want to use PHP to loop between this range and generate each IPv6 address that's within the range. I tried to turn binary to decimal to do the loop and increase the decimal number, but the loop does not produce any iteration.
Any help?
//The $IpStart_v6_FromDb/$IpStart_v6_End Vars are produced with INET6_ATON MariaDB function
$IpStartBin = decbin($IpStart_v6_FromDb);
$IpEndBin = decbin($IpEnd_v6_FromDb);
$ArIpRange = array();
$ArIpRange[] = $IpStartBin;
$x=0;
for(;;)
{
if ($IpStartBin==$IpEndBin) break;
$tLastIpBin = $ArIpRange[$x];
$tNextIpBin = decbin( (bindec($tLastIpBin) + 1) );
if ($tNextIpBin==$IpEndBin) break;
$ArIpRange[] = $tNextIpBin;
$x++;
}
foreach ($ArIpRange as $v)
{
echo "<br>IP range item:<br>".base64_encode($v); //debug
}
[EDIT]
I'm embarrassed to say that I thought the length of an IPv6 address is 64 bits.
So, some simple troubleshooting or reading the manual would have told you that decbin expects an integer as input. So right off the bat you're getting a zero back for both variables.
Furthermore, even if you did correct that problem (by using bindec,) you're talking about 128 bit numbers which, unless you're from the future, are not something PHP can handle natively.
I'd suggest handling them as strings. First normalize them (fill in missing zeroes and replace :: with zeroes) using code from this answer, find and remove the matching prefix using code from this answer, and then deal with the rest by converting them to much smaller numbers.
And, as mentioned in the comments, make sure you don't try dealing with too big a range or you will make your server unhappy.
<?php
// https://stackoverflow.com/a/55521768/1255289
// Generate a list of IPv6 addresses within a given range
function expand_ipv6(string $ip): ?string
{
// https://stackoverflow.com/a/12095836/1255289
$hex = unpack("H*", inet_pton($ip))[1] ?? "";
return (strlen($hex) === 32) ? implode(":", str_split($hex, 4)) : null;
}
$IpStart_v6_FromDb = "2001:db8::1234";
$IpEnd_v6_FromDb = "2001:db8::1299";
$ip1 = expand_ipv6($IpStart_v6_FromDb);
$ip2 = expand_ipv6($IpEnd_v6_FromDb);
if ($ip1 === null || $ip2 === null) {
die;
}
// https://stackoverflow.com/a/35838357/1255289
// length is 39 to account for 7 colons
for ($i = 0; $i < 39 && $ip1[$i] === $ip2[$i]; $i++);
$ipv6_prefix = substr($ip1, 0, $i);
$ipv6_start = hexdec(substr($ip1, $i));
$ipv6_end = hexdec(substr($ip2, $i));
if (strlen($ipv6_prefix) < 26) {
// adjust this to requirements to prevent too large ranges
die;
}
for ($a = $ipv6_start; $a <= $ipv6_end; $a++) {
$hex = dechex($a);
printf("%s%s\n", $ipv6_prefix, implode(":", str_split($hex, 4)));
}
Just like the title of this post says, I would to be able to check if every letter of a word is found in another word. So far these are the lines of codes that I was able to come up with:
<?php
$DBword = $_POST['DBword'];
$inputWords = $_POST['inputWords'];
$inputCount = str_word_count($inputWords,1);
echo "<b>THE WORD:</b>"."<br/>".$DBword."<br/><br/>";
echo "<b>WORDS ENTERED:</b><br/>";
foreach($inputCount as $outputWords)
{
echo $outputWords."<br/>";
}
foreach($inputCount as $countWords)
{
for($i=0; $i<strlen($countWords); $i++)
{$count = strpos( "$DBword", $countWords[$i]);}
if($count === false)
{
$score++;
}
}
echo "<b><br/>TOTAL SCORE: </b>";
echo $score;
?>
My point in having the foreach with the $outputWords is to just output the letters entered.
As for the other foreach that has $countWords, I am using it to really check if all letters in the word entered are found in the $DBword. I am using the for loop to check every letter.
So far, I am not getting the output that I want and I just ran out of ideas. Any ideas please?
function contains_letters($word1, $word2) {
for ($i = 0; $i < strlen($word1); $i++)
if (strpos($word2, $word1{$i}) === false)
return false;
return true;
}
//example usage
if (contains_letters($_POST['inputWords'], $_POST['DBword']))
echo "All the letters were found.";
If this check should be case-insensitive (i.e. 'A' counts as a usage of 'a'), change strpos to stripos.
Since you are overwriting $count in the for loop for each letter in $countWords, $count will contain the position of the last letter of $countWord only. Also, I am not sure why you increase score when the letter wasn't found.
In any case, you are making your life more difficult than necessary.
PHP has a function for counting chars in a string:
return count_chars($dbWord, 3) === count_chars($inputWord, 3);
will return true if the same letters are found in both strings.
Example to find all the words having exactly the same letters:
$dbWord = count_chars('foobar', 3);
$inputWords = 'barf boo oof raboof boarfo xyz';
print_r(
array_filter(
str_word_count($inputWords, 1),
function($inputWord) use ($dbWord) {
return count_chars($inputWord, 3) === $dbWord;
}
)
);
will output "raboof" and "boarfo" only.
How can I efficiently determine if a given string contains two strings?
For example, let's say I'm given the string: abc-def-jk-l. This string either contains two strings divided by a -, or it's not a match. The matching possibilities are:
Possible Matches for "abc-def-jk-l" :
abc def-jk-l
abc-def jk-l
abc-def-jk l
Now, here are my columns of strings to match:
Column I Column II
------- -------
1. abc-def A. qwe-rt
2. ghijkl B. yui-op
3. mn-op-qr C. as-df-gh
4. stuvw D. jk-l
How can I efficiently check to see if the given string matches two strings in the columns above? (The above is a match - matching abc-def and jk-l)
Here are some more examples:
abc-def-yui-op [MATCH - Matches 1-B]
abc-def-zxc-v [NO MATCH - Matches 1, but not any in column II.]
stuvw-jk-l [MATCH - Matches 4-D]
mn-op-qr-jk-l [Is this a match?]
Now, given a strings above, how can I efficiently determine matches? (Efficiency will be key, because columns i and ii will each have millions of rows on indexed columns in their respected tables!)
UPDATE: The order will always be column i, then column ii. (or "no match", which could mean it matches only one column or none)
Here's some php to help:
<?php
$arrStrings = array('abc-def-yui-op','abc-def-zxc-v','stuvw-jk-l','stuvw-jk-l');
foreach($arrStrings as $string) {
print_r(stringMatchCheck($string));
}
function stringMatchCheck($string) {
$arrI = array('abc-def','ghijkl','mn-op-qr','stuvw');
$arrII = array('qwe-rt','yui-op','as-df-gh','jk-l');
// magic stackoverflow help goes here!
if ()
return array($match[0],$match[1]);
else
return false;
}
?>
Just use PHP's strpos(). Loop until you find an entry from $arrI in $string using strpos(), and do the same for $arrII.
More info on strpos(): http://php.net/manual/en/function.strpos.php
EDIT:
To help you see what I'm talking about, here's your function:
function stringMatchCheck($string) {
$arrI = array('abc-def','ghijkl','mn-op-qr','stuvw');
$arrII = array('qwe-rt','yui-op','as-df-gh','jk-l');
$match = array(NULL, NULL);
// get match, if any, from first group
for ($i=0; $i<count($arrI) && !is_null($match[0]); $i++) {
if (strpos($string,$arrI[$i]) !== false) {
$match[0]=$arrI[$i];
}
}
if (!is_null($match[0])) {
// get match, if any, from second group group
for ($i=0; $i<count($arrII) && !is_null($match[1]); $i++) {
if (strpos($string,$arrII[$i]) !== false) {
$match[1]=$arrII[$i];
}
}
}
if (!is_null($match[0]) && !is_null($match[1])) {
return $match;
} else {
return false;
}
}
For efficiency sake, rather than loop through every entry in each column, split the string into as many different words as it takes and search for every word combination. Basically what you mention as possible matches.
$words = explode("-", $string);
$end = count($words) - 1;
for ( $i = 1; $i < $end; $i++ ) {
$partOne = array_slice($words, 0, $i);
$parttwo = array_slice($words, $i);
$wordOne = implode("-" , $partOne);
$wordTwo = implode("-" , $partTwo);
/* SQL to select $wordOne and $wordTwo from the tables */
}
I'm working with currencies so for example -
5 is OK as it is interpreted as 5.00. But 5.005 is not as it has too many digits after the point.
How can I restrict the amount of digits and show an error if there's too many?
Thanks
$x = '5.005'; // declare as string to avoid floating point errors
$parts = explode('.', $x);
if (strlen($parts[1]) > 2) {
die("Too many digits");
}
number_format will correct it for you, but if you want to error when too much precision is provided, you will need to test it.
$x = 12.345;
if ($x != number_format($x, 2)) {
// error!
}
You can format the number like this:
$num = 50695.3043;
$num = number_format( $num, 2, '.' );
echo $num;
This will result in:
50695.30
Note that this rounds. So 1.566 would round to 1.57.
I usually use sprintf
$formatted = sprintf("%01.2f", $price);
But there are many other functions / solutions you could use.
The following code will capture a number of things from a user-entered string:
too many decimal points, such as 1.2.3.
more than two digits in second section (if there), such as 1.234.
any non-numerics in first or second section, such as 123.4a or 1a3.45.
both first and second section empty, such as ..
$x = '12.34';
$parts = explode('.', $x);
$nm0a = preg_match ('/^[0-9]*$/', $parts[0]);
$nm0b = preg_match ('/^[0-9]+$/', $parts[0]);
$nm1a = preg_match ('/^[0-9]*$/', $parts[1]);
$nm1b = preg_match ('/^[0-9]+$/', $parts[1]);
if (count ($parts) > 2) { die ("Too many decimal points"); }
if ($nm0a == 0) { die ("Non-numeric first part"); }
if ($nm1a == 0) { die ("Non-numeric second part"); }
if (($nm0b == 0) && ($nm1b == 0)) { die ("Both parts empty"); }
if (strlen ($parts[1]) > 2) { die ("Too many digits after decimal point"); }
die ("Okay"); # Only here to provide output.