Creating a sequence from 1-ZZZZZZZZ in PHP - php

I am working on something where I need to generate the sequence 1,2,3...a,b,c,d...z,11,12,13...aa,ab,ac...zzzzzzzz, using php. This will only ever have to happen once, so it dosen't need to be very fast.
Thanks!

function incrementAlphanumeric($number) {
return base_convert(base_convert($number, 36, 10) + 1, 10, 36);
}
echo incrementAlphanumeric(9); // outputs "a"
To populate an array:
$number = 1;
$numbers = array();
while ($number != 'zzzzzzzz') {
$numbers[] = $number;
$number = incrementAlphanumeric($number);
}
http://php.net/base-convert

I recently had to do this with a non-standard set of character (they left out certain characters).
I put together a few functions I found on the net and got:
// this array misses a few letters due to the special naming convention
private $alphabet = array('0', '1','2','3','4','5','6','7','8','9','A','B','C','D','E','F','G','H','J','K','L','M','N','P','Q','R','S','T','U','V','W','X','Y','Z');
private function createDecimalFromCode($string){
$decimal = 0;
$base = count($this->alphabet);
$charset = implode($this->alphabet, '');
$charset = substr($charset, 0, $base);
do {
$char = substr($string, 0, 1);
$string = substr($string, 1);
$pos = strpos($charset, $char);
if ($pos === false) {
$error[] = "Illegal character ($char) in INPUT string";
return false;
} // if
$decimal = ($decimal * $base) + $pos;
} while($string <> null);
return $decimal;
}
private function createCodeFromDecimal($decimal){
$s = '';
while($decimal > 0) {
$s = $this->alphabet[$decimal%sizeof($this->alphabet)] . $s;
$decimal = floor($decimal/sizeof($this->alphabet));
}
return $s == '' ? '0' : $s;
}
Essentially I take my last created code, convert it to a decimal, add 1 and then convert that back to the next alphanumeric code.

Related

Creating licence plates from different but similar strings

I need help to create a licence plate (6 character length) from different equal or unequal length of strings.
Example 1:
$str1 = "YE37";
$str2 = "TE37";
$str3 = "LYTE";
When I combine, it should give me "LYTE37". I must use all of them to formulate a plate. I can find the common longest sequence between $str1 and $str2 is "E37" but unsure "Y" or "T" comes first (i.e., whether "YTE37" or "TYE37")" then I can combine with $str3 using the longest common sequence ("YTE") which supposed to give me "LYTE37".
Example 2: "YLF3", "EYLF" and "YLF37" should give me "EYLF37".
I use the following function that finds the longest common sequence
$string_1="YE37";
$string_2="TE37";
$S =get_longest_common_subsequence($string_1, $string_2); // $S is "E37"
function get_longest_common_subsequence($string_1, $string_2)
{
$string_1_length = strlen($string_1);
$string_2_length = strlen($string_2);
$return = '';
if ($string_1_length === 0 || $string_2_length === 0)
{
// No similarities
return $return;
}
$longest_common_subsequence = array();
// Initialize the CSL array to assume there are no similarities
$longest_common_subsequence = array_fill(0, $string_1_length, array_fill(0, $string_2_length, 0));
$largest_size = 0;
for ($i = 0; $i < $string_1_length; $i++)
{
for ($j = 0; $j < $string_2_length; $j++)
{
// Check every combination of characters
if ($string_1[$i] === $string_2[$j])
{
// These are the same in both strings
if ($i === 0 || $j === 0)
{
// It's the first character, so it's clearly only 1 character long
$longest_common_subsequence[$i][$j] = 1;
}
else
{
// It's one character longer than the string from the previous character
$longest_common_subsequence[$i][$j] = $longest_common_subsequence[$i - 1][$j - 1] + 1;
}
if ($longest_common_subsequence[$i][$j] > $largest_size)
{
// Remember this as the largest
$largest_size = $longest_common_subsequence[$i][$j];
// Wipe any previous results
$return = '';
// And then fall through to remember this new value
}
if ($longest_common_subsequence[$i][$j] === $largest_size)
{
// Remember the largest string(s)
$return = substr($string_1, $i - $largest_size + 1, $largest_size);
}
}
// Else, $CSL should be set to 0, which it was already initialized to
}
}
// Return the list of matches
return $return;
}
I need an algorithm that uses these strings and creates a licence plate.
Could this be the Algorithm you are looking for? Quick-Test Here.
<?php
$str1 = "YE37";
$str2 = "TE37";
$str3 = "LYTE";
$strA = "YLF3";
$strB = "EYLF";
$strC = "YLF37";
function generatePlateNumber($str1, $str2, $str3) {
$plateNumber = '';
$arr = array($str1, $str2, $str3);
$arrStr = array();
foreach($arr as $str){
if(!preg_match("#\d#", $str)){
$arrStr[] = $str;
}
}
foreach($arr as $str){
if(preg_match("#\d#", $str)){
$arrStr[] = $str;
}
}
$chars = array_merge(str_split($arrStr[0]),
str_split($arrStr[1]),
str_split($arrStr[2]) );
$alphabets = [];
$numbers = [];
foreach($chars as $char){
if(is_numeric($char)){
$numbers[] = $char;
}else{
$alphabets[] = $char;
}
}
$alphabets = array_unique($alphabets);
$numbers = array_unique($numbers);
// BUILD THE PLATE NUMBER:
$plateNumber .= implode($alphabets) . implode($numbers);
return $plateNumber;
}

convert emoji to their hex code

I'm trying to detect the emoji that I get through e.g. a POST (the source ist not necessary).
As an example I'm using this emoji: ✊🏾 (I hope it's visible)
The code for it is U+270A U+1F3FE (I'm using http://unicode.org/emoji/charts/full-emoji-list.html for the codes)
Now I converted the emoji with json_encode and I get: \u270a\ud83c\udffe
Here the only part that is equal is 270a. \ud83c\udffe is not equal to U+1F3FE, not even if I add them together (1B83A)
How do I get from ✊🏾 to U+270A U+1F3FE with e.g. php?
Use mb_convert_encoding and convert from UTF-8 to UTF-32. Then do some additional formatting:
// Strips leading zeros
// And returns str in UPPERCASE letters with a U+ prefix
function format($str) {
$copy = false;
$len = strlen($str);
$res = '';
for ($i = 0; $i < $len; ++$i) {
$ch = $str[$i];
if (!$copy) {
if ($ch != '0') {
$copy = true;
}
// Prevent format("0") from returning ""
else if (($i + 1) == $len) {
$res = '0';
}
}
if ($copy) {
$res .= $ch;
}
}
return 'U+'.strtoupper($res);
}
function convert_emoji($emoji) {
// ✊🏾 --> 0000270a0001f3fe
$emoji = mb_convert_encoding($emoji, 'UTF-32', 'UTF-8');
$hex = bin2hex($emoji);
// Split the UTF-32 hex representation into chunks
$hex_len = strlen($hex) / 8;
$chunks = array();
for ($i = 0; $i < $hex_len; ++$i) {
$tmp = substr($hex, $i * 8, 8);
// Format each chunk
$chunks[$i] = format($tmp);
}
// Convert chunks array back to a string
return implode($chunks, ' ');
}
echo convert_emoji('✊🏾'); // U+270A U+1F3FE
Simple function, inspired by #d3L answer above
function emoji_to_unicode($emoji) {
$emoji = mb_convert_encoding($emoji, 'UTF-32', 'UTF-8');
$unicode = strtoupper(preg_replace("/^[0]+/","U+",bin2hex($emoji)));
return $unicode;
}
Exmaple
emoji_to_unicode("💵");//returns U+1F4B5
You can do like this, consider the emoji a normal character.
$emoji = "✊🏾";
$str = str_replace('"', "", json_encode($emoji, JSON_HEX_APOS));
$myInput = $str;
$myHexString = str_replace('\\u', '', $myInput);
$myBinString = hex2bin($myHexString);
print iconv("UTF-16BE", "UTF-8", $myBinString);

Remove random generated characters from a string to display the word in clear text

I have this function to put some random characters into a string:
function random($string) {
$chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';
$shuffle_start = substr(str_shuffle($chars), 0, 6);
$shuffle_end = substr(str_shuffle($chars), 0, 6);
$letters = str_split($string);
$str = '';
$count = count($letters);
foreach($letters AS $l) {
$count--;
$str .= $l;
if($count) {
$str .= substr(str_shuffle($chars), 0, 5);
}
}
return $shuffle_start . $str . $shuffle_end;
}
This function prints this from the string "hello": aApi3VhKJrDjeAbCkalprX7ll7N0Qjo3qymiw. Now, I want to remove the random characters from the string so the word "hello" are being clearly seen.
How can I do this?
Just move backwards. Strip 6 characters form start and end, and then get every sixth character
function unrandom($str){
$base = substr($str, 6, strlen($str)-12);
$ret = '';
for($i=0;$i < strlen($base); $i+=6) {
$ret .= substr($base, $i,1);
}
return $ret;
}

An encoding scheme to shorten a string of numbers that is url safe

I have a string that looks like this (contains numbers, periods and dashes):
1372137673.276886940002-19690324617-19694854617-18953258947
Since I only have numbers, periods and dashes, I would like to use an url-safe (only numbers and letters) encoding scheme to shorten it. I also need to be able to reverse the encoded string to its original form.
I have had a look at base64, but it increases the size of the string by a fair bit, which is not what I want.
I plan to have this implemented in PHP and Javascript.
Are there any existing schemes that can do this? My main motivation is to make the above string shorter and the result should be URL safe.
Convert the numbers into their binary form, then Base64 encode that.
One reasonable attempt would be:
break down the string into tokens separated by dashes and dots
convert each token to a higher base (36 is a base which you can convert to and from easily from both JS and PHP)
join the tokens back together -- both dashes and dots are valid in a URL
However, the "have to do this in JS also" requirement seems a bit suspect -- why does your client-side code have to extract information from URLs which are ultimately under the server's authority? In general URLs should be opaque and when that's not true alarm bells should start ringing.
To do it in javascript also, you need the help of http://phpjs.org/ :)
i think all php functions i used in this script is available there, like bcomp
You can tweak this code to get an even smaller string, bit busy now, if i got time, sure i will update this answer :)
<?php
/**
* This function will encode a larger number to small string
**/
function encode( $int = null ) {
$chars = 'kwn7uh2qifbj8te9vp64zxcmayrg50ds31';
$uid = '';
while( bccomp( $int, 0, 0) != 0 ) {
$rem = bcmod( $int, 34 );
$int = bcdiv( bcsub( $int, $rem, 0 ), 34, 0 );
$uid = $chars[$rem].$uid;
}
return $uid;
}
/**
* This function will decode a string encoded with above function to its original state
**/
function decode( $uid = null ) {
$chars = 'kwn7uh2qifbj8te9vp64zxcmayrg50ds31';
$id = '';
$len = strlen( $uid );
for( $i = $len - 1; $i >= 0; $i-- ) {
$value = strpos( $chars, $uid[$i] );
$id = bcadd( $id, bcmul( $value, bcpow( 34, ( $len - $i - 1 ) ) ) );
}
return $id;
}
/**
* Below function is only for your needs
**/
function int_to_str( $str = null, $decode = false ) {
//$len = array(); // reserved for further updates :)
$numbers1 = explode( "-", $str );
foreach( $numbers1 as &$num1 ) {
$func = ( $decode ) ? "decode" : "encode";
$num1 = implode( ".", array_map( $func, explode( ".", $num1 ) ) );
}
$numbers1 = implode( "-", $numbers1 );
return $numbers1;
}
// Encode your numbers to short strings
$str = int_to_str( "1372137673.276886940002-19690324617-19694854617-18953258947" );
// Decode your encoded string to its original state
$int = int_to_str( $str, true );
echo $str."<br />";
echo $int;
?>
Just because it was fun to do... it encodes the string on a custom base 64 keeping the separators intact:
function encode_token($digit) {
if ($digit < 10)
return (string) $digit;
if ($digit < 36)
return chr(ord('A') + ($digit - 10));
if ($digit < 62)
return chr(ord('a') + ($digit - 36));
if ($digit == 62) return ',';
return '+';
}
function encode_value($value) {
if (in_array($value, array('.', '-'))) return $value;
$int = (int) $value;
$encoded = '';
while($int) {
$encoded .= encode_token($int & 0x3F);
$int >>= 6;
}
return $encoded;
}
function encode($string) {
$values = preg_split(',([\.-]),', $string, -1, PREG_SPLIT_DELIM_CAPTURE);
$encoded = '';
foreach($values as $value)
$encoded .= encode_value($value);
return $encoded;
}
function decode_token($token) {
if ($token <= '9') return (int) $token;
if ($token <= 'Z') return 10 + ord($token) - ord('A');
if ($token <= 'z') return 36 + ord($token) - ord('a');
if ($token == ',') return 62;
return 63;
}
function decode_value($value) {
if (in_array($value, array('.', '-'))) return $value;
$decoded = 0;
for($i = strlen($value) - 1;$i >= 0;$i--) {
$decoded <<= 6;
$decoded |= decode_token($value[$i]);
}
return $decoded;
}
function decode($string) {
$values = preg_split(',([\.-]),', $string, -1, PREG_SPLIT_DELIM_CAPTURE);
$decoded = '';
foreach($values as $value)
$decoded .= decode_value($value);
return $decoded;
}
$string = '1372137673.276886940002-19690324617-19694854617-18953258947';
echo $string . PHP_EOL;
$encoded = encode($string);
echo $encoded . PHP_EOL;
$decoded = decode($encoded);
echo $decoded . PHP_EOL;

keyword highlight is highlighting the highlights in PHP preg_replace()

I have a small search engine doing its thing, and want to highlight the results. I thought I had it all worked out till a set of keywords I used today blew it out of the water.
The issue is that preg_replace() is looping through the replacements, and later replacements are replacing the text I inserted into previous ones. Confused? Here is my pseudo function:
public function highlightKeywords ($data, $keywords = array()) {
$find = array();
$replace = array();
$begin = "<span class=\"keywordHighlight\">";
$end = "</span>";
foreach ($keywords as $kw) {
$find[] = '/' . str_replace("/", "\/", $kw) . '/iu';
$replace[] = $begin . "\$0" . $end;
}
return preg_replace($find, $replace, $data);
}
OK, so it works when searching for "fred" and "dagg" but sadly, when searching for "class" and "lass" and "as" it strikes a real issue when highlighting "Joseph's Class Group"
Joseph's <span class="keywordHighlight">Cl</span><span <span c<span <span class="keywordHighlight">cl</span>ass="keywordHighlight">lass</span>="keywordHighlight">c<span <span class="keywordHighlight">cl</span>ass="keywordHighlight">lass</span></span>="keywordHighlight">ass</span> Group
How would I get the latter replacements to only work on the non-HTML components, but to also allow the tagging of the whole match? e.g. if I was searching for "cla" and "lass" I would want "class" to be highlighted in full as both the search terms are in it, even though they overlap, and the highlighting that was applied to the first match has "class" in it, but that shouldn't be highlighted.
Sigh.
I would rather use a PHP solution than a jQuery (or any client-side) one.
Note: I have tried to sort the keywords by length, doing the long ones first, but that means the cross-over searches do not highlight, meaning with "cla" and "lass" only part of the word "class" would highlight, and it still murdered the replacement tags :(
EDIT: I have messed about, starting with pencil & paper, and wild ramblings, and come up with some very unglamorous code to solve this issue. It's not great, so suggestions to trim/speed this up would still be greatly appreciated :)
public function highlightKeywords ($data, $keywords = array()) {
$find = array();
$replace = array();
$begin = "<span class=\"keywordHighlight\">";
$end = "</span>";
$hits = array();
foreach ($keywords as $kw) {
$offset = 0;
while (($pos = stripos($data, $kw, $offset)) !== false) {
$hits[] = array($pos, $pos + strlen($kw));
$offset = $pos + 1;
}
}
if ($hits) {
usort($hits, function($a, $b) {
if ($a[0] == $b[0]) {
return 0;
}
return ($a[0] < $b[0]) ? -1 : 1;
});
$thisthat = array(0 => $begin, 1 => $end);
for ($i = 0; $i < count($hits); $i++) {
foreach ($thisthat as $key => $val) {
$pos = $hits[$i][$key];
$data = substr($data, 0, $pos) . $val . substr($data, $pos);
for ($j = 0; $j < count($hits); $j++) {
if ($hits[$j][0] >= $pos) {
$hits[$j][0] += strlen($val);
}
if ($hits[$j][1] >= $pos) {
$hits[$j][1] += strlen($val);
}
}
}
}
}
return $data;
}
I've used the following to address this problem:
<?php
$protected_matches = array();
function protect(&$matches) {
global $protected_matches;
return "\0" . array_push($protected_matches, $matches[0]) . "\0";
}
function restore(&$matches) {
global $protected_matches;
return '<span class="keywordHighlight">' .
$protected_matches[$matches[1] - 1] . '</span>';
}
preg_replace_callback('/\x0(\d+)\x0/', 'restore',
preg_replace_callback($patterns, 'protect', $target_string));
The first preg_replace_callback pulls out all matches and replaces them with nul-byte-wrapped placeholders; the second pass replaces them with the span tags.
Edit: Forgot to mention that $patterns was sorted by string length, longest to shortest.
Edit; another solution
<?php
function highlightKeywords($data, $keywords = array(),
$prefix = '<span class="hilite">', $suffix = '</span>') {
$datacopy = strtolower($data);
$keywords = array_map('strtolower', $keywords);
$start = array();
$end = array();
foreach ($keywords as $keyword) {
$offset = 0;
$length = strlen($keyword);
while (($pos = strpos($datacopy, $keyword, $offset)) !== false) {
$start[] = $pos;
$end[] = $offset = $pos + $length;
}
}
if (!count($start)) return $data;
sort($start);
sort($end);
// Merge and sort start/end using negative values to identify endpoints
$zipper = array();
$i = 0;
$n = count($end);
while ($i < $n)
$zipper[] = count($start) && $start[0] <= $end[$i]
? array_shift($start)
: -$end[$i++];
// EXAMPLE:
// [ 9, 10, -14, -14, 81, 82, 86, -86, -86, -90, 99, -103 ]
// take 9, discard 10, take -14, take -14, create pair,
// take 81, discard 82, discard 86, take -86, take -86, take -90, create pair
// take 99, take -103, create pair
// result: [9,14], [81,90], [99,103]
// Generate non-overlapping start/end pairs
$a = array_shift($zipper);
$z = $x = null;
while ($x = array_shift($zipper)) {
if ($x < 0)
$z = $x;
else if ($z) {
$spans[] = array($a, -$z);
$a = $x;
$z = null;
}
}
$spans[] = array($a, -$z);
// Insert the prefix/suffix in the start/end locations
$n = count($spans);
while ($n--)
$data = substr($data, 0, $spans[$n][0])
. $prefix
. substr($data, $spans[$n][0], $spans[$n][1] - $spans[$n][0])
. $suffix
. substr($data, $spans[$n][1]);
return $data;
}
I had to revisit this subject myself today and wrote a better version of the above. I'll include it here. It's the same idea only easier to read and should perform better since it uses arrays instead of concatenation.
<?php
function highlight_range_sort($a, $b) {
$A = abs($a);
$B = abs($b);
if ($A == $B)
return $a < $b ? 1 : 0;
else
return $A < $B ? -1 : 1;
}
function highlightKeywords($data, $keywords = array(),
$prefix = '<span class="highlight">', $suffix = '</span>') {
$datacopy = strtolower($data);
$keywords = array_map('strtolower', $keywords);
// this will contain offset ranges to be highlighted
// positive offset indicates start
// negative offset indicates end
$ranges = array();
// find start/end offsets for each keyword
foreach ($keywords as $keyword) {
$offset = 0;
$length = strlen($keyword);
while (($pos = strpos($datacopy, $keyword, $offset)) !== false) {
$ranges[] = $pos;
$ranges[] = -($offset = $pos + $length);
}
}
if (!count($ranges))
return $data;
// sort offsets by abs(), positive
usort($ranges, 'highlight_range_sort');
// combine overlapping ranges by keeping lesser
// positive and negative numbers
$i = 0;
while ($i < count($ranges) - 1) {
if ($ranges[$i] < 0) {
if ($ranges[$i + 1] < 0)
array_splice($ranges, $i, 1);
else
$i++;
} else if ($ranges[$i + 1] < 0)
$i++;
else
array_splice($ranges, $i + 1, 1);
}
// create substrings
$ranges[] = strlen($data);
$substrings = array(substr($data, 0, $ranges[0]));
for ($i = 0, $n = count($ranges) - 1; $i < $n; $i += 2) {
// prefix + highlighted_text + suffix + regular_text
$substrings[] = $prefix;
$substrings[] = substr($data, $ranges[$i], -$ranges[$i + 1] - $ranges[$i]);
$substrings[] = $suffix;
$substrings[] = substr($data, -$ranges[$i + 1], $ranges[$i + 2] + $ranges[$i + 1]);
}
// join and return substrings
return implode('', $substrings);
}
// Example usage:
echo highlightKeywords("This is a test.\n", array("is"), '(', ')');
echo highlightKeywords("Classes are as hard as they say.\n", array("as", "class"), '(', ')');
// Output:
// Th(is) (is) a test.
// (Class)es are (as) hard (as) they say.
OP - something that's not clear in the question is whether $data can contain HTML from the get-go. Can you clarify this?
If $data can contain HTML itself, you are getting into the realms attempting to parse a non-regular language with a regular language parser, and that's not going to work out well.
In such a case, I would suggest loading the $data HTML into a PHP DOMDocument, getting hold of all of the textNodes and running one of the other perfectly good answers on the contents of each text block in turn.

Categories