php str_shuffle does wrong encoding on greek letters - php

I'm working on a project where I use php to grab a random greek word from a xampp sql server . I then use str_shuffle() to randomize the word order (ex. bye => ybe).However using str_shuffle() on greek letters returns the word with many ???? in place of most greek letters . If I remove str_shuffle() from my code the greek word is displayed correctly with no ??? .
I have written code that ensures I have the correct encoding but str_shuffle() is the problem .
<h1 id = "hidden-word">The word is :
<?php
$link = mysqli_connect('localhost' , 'root' , '' ,'dodecanese');
if(!$link){
echo 'Error connecting to DB';
exit;
}
mysqli_query($link,"SET NAMES 'utf8'");
$query = "SELECT island_name FROM dodecanese ORDER BY RAND() LIMIT 1";
$result = mysqli_query($link, $query);
if(!$result){
echo 'There is an issue with the DB';
exit;
}
$row = mysqli_fetch_assoc($result);
//str shuffle creates ?
echo '<span id = "random-island">'.str_shuffle($row['island_name']). '</span>';
?>
</h1>
I also have encoding <meta charset="utf-8"/> on html . I have seen many posts about this and especially the UTF-8 all the way through but it did not help . I would appreciate your help with this . Thank you in advance .

I've looked in the the PHP manual for str_shuffle and found out in the comments that indeed there are problems with some unicode chars.
But there is also a solution there - which I've tested for you, and it works:
<?php
function str_shuffle_unicode($str) {
$tmp = preg_split("//u", $str, -1, PREG_SPLIT_NO_EMPTY);
shuffle($tmp);
return join("", $tmp);
}
$a = "γκξπψ";
$b = str_shuffle($a);
$c = str_shuffle_unicode($a);
echo $a; // γκξπψ
echo "<br/>str_shuffle: ".$b; // ξ��κ�ψ�
echo "<br/>str_shuffle_unicode: ".$c; // κξγπψ
?>

Unfortunately str_shuffle() does not work with multibyte characters and there is no (or at least I do not know such) built-in function to do that. As a workaround you can write one your own. For example the below code will split the string into array of single letters, shuffle the array and then join its elements back to string (I used Cyrillic letters for the example):
$str = "абвгдежзий";
$temp = mb_str_split($str,1);
shuffle($temp);
$str = join("", $temp);
echo $str;
The above function mb_str_split is a PHP 7.4+ only. If you are using earlier version, you can use preg_split:
$str = "абвгдежзий";
$temp = preg_split("//u", $str, 0);
shuffle($temp);
$str = join("", $temp);
echo $str;
the more uncomfortable preg_match_all:
$str = "абвгдежзий";
preg_match_all('/./u', $str, $temp);
shuffle($temp[0]);
$str = join("", $temp[0]);
echo $str;
or even looping and adding to the array char-by-char (that way you save a regex call):
$str = "абвгдежзий";
$len = mb_strlen($str, 'UTF-8');
$temp = [];
for ($i = 0; $i < $len; $i++) {
$temp[] = mb_substr($str, $i, 1, 'UTF-8');
}
shuffle($temp);
$str = join("", $temp);
echo $str;

Related

How to find out if there is any redundant word in string in php?

I need to find out if there are any redundant words in string or not .Is there any function that can provide me result in true/false.
Example:
$str = "Hey! How are you";
$result = redundant($str);
echo $result ; //result should be 0 or false
But for :
$str = "Hey! How are are you";
$result = redundant($str);
echo $result ; //result should be 1 or true
Thank you
You could use explode to generate an array containing all words in your string:
$array = explode(" ", $str);
Than you could prove if the arrays contains duplicates with the function provided in this answer:
https://stackoverflow.com/a/3145660/5420511
I think this is what you are trying to do, this splits on punctuation marks or whitespaces. The commented out lines can be used if you want the duplicated words:
$str = "Hey! How are are you?";
$output = redundant($str);
echo $output;
function redundant($string){
$words = preg_split('/[[:punct:]\s]+/', $string);
if(max(array_count_values($words)) > 1) {
return 1;
} else {
return 0;
}
//foreach(array_count_values($words) as $word => $count) {
// if($count > 1) {
// echo '"' . $word . '" is in the string more than once';
// }
//}
}
References:
http://php.net/manual/en/function.array-count-values.php
http://php.net/manual/en/function.max.php
http://php.net/manual/en/function.preg-split.php
Regex Demo: https://regex101.com/r/iH0eA6/1

i want text and numeric part from the string in php

i have one string
$str ='california 94063';
now i want california and 94063 both in diferent variable.
string can be anything
Thanks in advance....
How about
$strings = explode(' ', $str);
Assuming that your string has ' ' as a separator.
Then, if you want to find the numeric entries of the $strings array, you can use is_numeric function.
Do like this
list($str1,$str2)=explode(' ',$str);
echo $str2;
If your string layout is always the same (say: follows a given format) then I'd use sscanf (http://www.php.net/manual/en/function.sscanf.php).
list($str, $number) = sscanf('california 94063, "%str %d");
<?php
$str ='california 94063';
$x = preg_match('(([a-zA-Z]*) ([0-9]*))',$str, $r);
echo 'String Part='. $r[1];
echo "<br />";
echo 'Number Part='.$r[2];
?>
If text pattern can be changed then I found this solution
Source ::
How to separate letters and digits from a string in php
<?php
$string="94063 california";
$chars = '';
$nums = '';
for ($index=0;$index<strlen($string);$index++) {
if(isNumber($string[$index]))
$nums .= $string[$index];
else
$chars .= $string[$index];
}
echo "Chars: -".trim($chars)."-<br>Nums: -".trim($nums)."-";
function isNumber($c) {
return preg_match('/[0-9]/', $c);
}
?>

Replace character's position in a string

In PHP, how can you replace the second and third character of a string with an X so string would become sXXing?
The string's length would be fixed at six characters.
Thanks
It depends on what you are doing.
In most cases, you will use :
$string = "string";
$string[1] = "X";
$string[2] = "X";
This will sets $string to "sXXing", as well as
substr_replace('string', 'XX', 1, 2);
But if you want a prefect way to do such a cut, you should be aware of encodings.
If your $string is 我很喜欢重庆, your output will be "�XX很喜欢" instead of "我XX欢重庆".
A "perfect" way to avoid encoding problems is to use the PHP MultiByte String extension.
And a custom mb_substr_replace because it has not been already implemented :
function mb_substr_replace($output, $replace, $posOpen, $posClose) {
return mb_substr($output, 0, $posOpen) . $replace . mb_substr($output, $posClose + 1);
}
Then, code :
echo mb_substr_replace('我很喜欢重庆', 'XX', 1, 2);
will show you 我XX欢重庆.
Simple:
<?php
$str = "string";
$str[1] = $str[2] = "X";
echo $str;
?>
For replacing, use function
$str = 'bar';
$str[1] = 'A';
echo $str; // prints bAr
or you could use the library function substr_replace as:
$str = substr_replace($str,$char,$pos,1);
similarly for 3rd position
function mb_substr_replace($string, $replacement, $start, $length=0)
{
return mb_substr($string, 0, $start) . $replacement . mb_substr($string, $start+$length);
}
same as above, but standardized to be more like substr_replace (-substr- functions usually take length, not end position)

Why my typeset function doesn't work for non-latin/Asian characters?

I've convinced my boss to do the typesetting stuff using PHP(PHP Version 5.2.8).
And this is what I got so far(set Character encoding to Unicode(UTF-8) if you see misrendered Japanese characters):
demo page at my personal website
Basically, if you copy and paste the latin sample paragraph into the textarea and click the button, everything works well, you can verify that by pasting the result into Notepad for a check(albeit the fact that I haven't done anything to use hyphens to denote words separated by new lines).
However, when it comes with non-latin/Asian characters, nothing got printed out. I didn't get any error message generated, just cannot see anything at all...
The following is my code:
<?php
$words = typesetWords($_POST['words']);
echo json_encode(array('feedback' => $words));
function typesetWords($words, $lineLength = 70)
{
try
{
$result = '';
$paragraphs = explode("\n\n", $words);
foreach($paragraphs as $paragraph)
{
$paragraph = str_replace("\n", "", $paragraph);
$length = strlen($paragraph);
$numberOfLines = intval($length / $lineLength);
$tmp = '';
if($numberOfLines > 0)
{
for($i = 0; $i < $numberOfLines; $i++)
$tmp .= substr($paragraph, $i * $lineLength, $lineLength)."\n";
$tmp .= substr($paragraph, -1 * ($length % $lineLength))."\n\n";
$result .= $tmp;
}
else $result .= $paragraph."\n\n";
}
}
catch(Exception $e)
{
return $e->getMessage();
}
return $result;
}
?>
I tried to return what was sent by the form directly back, and I did see the Japanese sample paragraph without problems. So I reckon one of the PHP library functions must have
caused the error, but I couldn't tell which one and how to fix it...
Many thanks in advance!
strlen() will return the number of characters from a string formatted for ANSI/ASCII, not UTF-8. Try mb_strlen() instead.

hangman "word" in php

I am trying to do something similar to hangman where when you guess a letter, it replaces an underscore with what the letter is. I have come up with a way, but it seems very inefficient and I am wondering if there is a better way. Here is what I have -
<?
$word = 'ball';
$lettersGuessed = array('b','a');
echo str_replace( $lettersGuessed , '_' , $word ); // __ll
echo '<br>';
$wordArray = str_split ( $word );
foreach ( $wordArray as $letterCheck )
{
if ( in_array( $letterCheck, $lettersGuessed ) )
{
$finalWord .= $letterCheck;
} else {
$finalWord .= '_';
}
}
echo $finalWord; // ba__
?>
str_replace does the opposite of what I want. I want what the value of $finalWord is without having to go through a loop to get the result I desire.
If I am following you right you want to do the opposite of the first line:
echo str_replace( $lettersGuessed , '_' , $word ); // __ll
Why not create an array of $opposite = range('a', 'z'); and then use array_diff () against $lettersGuessed, which will give you an array of unguessed letters. It would certainly save a few lines of code. Such as:
$all_letters = range('a', 'z');
$unguessed = array_diff ($all_letters, $lettersGuessed);
echo str_replace( $unguessed , '_' , $word ); // ba__
It's an array, foreach is what you're suppose to be doing, it's lightning fast anyways, I think you are obsessing over something that's not even a problem.
You want to use an array becuase you can easily tell which indexes in the array are the ones that contain the letter, which directly correlates to which place in the string the _ should become a letter.
Your foreach loop is a fine way to do it. It won't be slow because your words will never be huge.
You can also create a regex pattern with the guessed letters to replace everything except those letters. Like this:
$word = 'ball';
$lettersGuessed = array('b','a');
$pattern = '/[^' . implode('', $lettersGuessed) . ']/'; // results in '/[^ba]/
$maskedWord = preg_replace($pattern, '_', $word);
echo $maskedWord;
Another way would be to access the string as an array, e.g.
$word = 'ball';
$length = strlen($word);
$mask = str_pad('', $length, '_');
$guessed = 'l';
for($i = 0; $i < $length; $i++) {
if($word[$i] === $guessed) {
$mask[$i] = $guessed;
}
}
echo $mask; // __ll

Categories