PHP, str_pad unicode issue
Solution 1
I think you need to look inside more php.net (here: http://php.net/str_pad#111147). But I changed it a bit.
Note: Don't forget to call this before mb_internal_encoding("utf-8");
.
mb_internal_encoding("utf-8");
function str_pad_unicode($str, $pad_len, $pad_str = ' ', $dir = STR_PAD_RIGHT) {
$str_len = mb_strlen($str);
$pad_str_len = mb_strlen($pad_str);
if (!$str_len && ($dir == STR_PAD_RIGHT || $dir == STR_PAD_LEFT)) {
$str_len = 1; // @debug
}
if (!$pad_len || !$pad_str_len || $pad_len <= $str_len) {
return $str;
}
$result = null;
if ($dir == STR_PAD_BOTH) {
$length = ($pad_len - $str_len) / 2;
$repeat = ceil($length / $pad_str_len);
$result = mb_substr(str_repeat($pad_str, $repeat), 0, floor($length))
. $str
. mb_substr(str_repeat($pad_str, $repeat), 0, ceil($length));
} else {
$repeat = ceil($str_len - $pad_str_len + $pad_len);
if ($dir == STR_PAD_RIGHT) {
$result = $str . str_repeat($pad_str, $repeat);
$result = mb_substr($result, 0, $pad_len);
} else if ($dir == STR_PAD_LEFT) {
$result = str_repeat($pad_str, $repeat);
$result = mb_substr($result, 0,
$pad_len - (($str_len - $pad_str_len) + $pad_str_len))
. $str;
}
}
return $result;
}
$t = STR_PAD_LEFT;
$s = '...';
$as = 'AO';
$ms = 'ÄÖ';
echo "<pre>\n";
for ($i = 3; $i <= 1000; $i++) {
$s1 = str_pad($s, $i, $as, $t); // can not inculde unicode char!!!
$s2 = str_pad_unicode($s, $i, $ms, $t);
$l1 = strlen($s1);
$l2 = mb_strlen($s2);
echo "len $l1: $s1 \n";
echo "len $l2: $s2 \n";
echo "\n";
if ($l1 != $l2) die("Fail!");
}
echo "</pre>";
Test here: http://codepad.viper-7.com/3jTEgt
Solution 2
You need a multibyte version of str_pad()
, like below. It's inspired by the source code of str_pad()
.
function mb_str_pad($input, $pad_length, $pad_string = ' ', $pad_type = STR_PAD_RIGHT, $encoding = 'UTF-8')
{
$input_length = mb_strlen($input, $encoding);
$pad_string_length = mb_strlen($pad_string, $encoding);
if ($pad_length <= 0 || ($pad_length - $input_length) <= 0) {
return $input;
}
$num_pad_chars = $pad_length - $input_length;
switch ($pad_type) {
case STR_PAD_RIGHT:
$left_pad = 0;
$right_pad = $num_pad_chars;
break;
case STR_PAD_LEFT:
$left_pad = $num_pad_chars;
$right_pad = 0;
break;
case STR_PAD_BOTH:
$left_pad = floor($num_pad_chars / 2);
$right_pad = $num_pad_chars - $left_pad;
break;
}
$result = '';
for ($i = 0; $i < $left_pad; ++$i) {
$result .= mb_substr($pad_string, $i % $pad_string_length, 1, $encoding);
}
$result .= $input;
for ($i = 0; $i < $right_pad; ++$i) {
$result .= mb_substr($pad_string, $i % $pad_string_length, 1, $encoding);
}
return $result;
}
$str = "nü";
$pad = "ü";
echo mb_str_pad($str, 5, $pad);
Solution 3
Try this (It may look like the one that failed, but this has an encoding check as well):
<?php
function mb_str_pad ($input, $pad_length, $pad_string, $pad_style, $encoding="UTF-8") {
return str_pad($input, strlen($input)-mb_strlen($input,$encoding)+$pad_length, $pad_string, $pad_style);
}
?>
Solution 4
My contribution to this.
/**
* Multibyte String Pad
*
* Functionally, the equivalent of the standard str_pad function, but is capable of successfully padding multibyte strings.
*
* @param string $input The string to be padded.
* @param int $length The length of the resultant padded string.
* @param string $padding The string to use as padding. Defaults to space.
* @param int $padType The type of padding. Defaults to STR_PAD_RIGHT.
* @param string $encoding The encoding to use, defaults to UTF-8.
*
* @return string A padded multibyte string.
*/
function mb_str_pad($input, $length, $padding = ' ', $padType = STR_PAD_RIGHT, $encoding = 'UTF-8')
{
$result = $input;
if (($paddingRequired = $length - mb_strlen($input, $encoding)) > 0) {
switch ($padType) {
case STR_PAD_LEFT:
$result =
mb_substr(str_repeat($padding, $paddingRequired), 0, $paddingRequired, $encoding).
$input;
break;
case STR_PAD_RIGHT:
$result =
$input.
mb_substr(str_repeat($padding, $paddingRequired), 0, $paddingRequired, $encoding);
break;
case STR_PAD_BOTH:
$leftPaddingLength = floor($paddingRequired / 2);
$rightPaddingLength = $paddingRequired - $leftPaddingLength;
$result =
mb_substr(str_repeat($padding, $leftPaddingLength), 0, $leftPaddingLength, $encoding).
$input.
mb_substr(str_repeat($padding, $rightPaddingLength), 0, $rightPaddingLength, $encoding);
break;
}
}
return $result;
}
Unit test method
/**
* @dataProvider provideDataForMbStrPad
*
* @param string $input
* @param int $length
* @param string $padding
* @param int $padType
* @param string $result
*/
public function testMbStrPad($input, $length, $padding, $padType, $result)
{
$this->assertEquals($result, Strings::mbStrPad($input, $length, $padding, $padType));
}
Data provider for above unit test
public function provideDataForMbStrPad()
{
return [
['Nhiều byte string đệm', 0, ' ', STR_PAD_BOTH, 'Nhiều byte string đệm'],
['Nhiều byte string đệm', 0, ' ', STR_PAD_LEFT, 'Nhiều byte string đệm'],
['Nhiều byte string đệm', 0, ' ', STR_PAD_RIGHT, 'Nhiều byte string đệm'],
['Nhiều byte string đệm', 0, '充', STR_PAD_BOTH, 'Nhiều byte string đệm'],
['Nhiều byte string đệm', 0, '充', STR_PAD_LEFT, 'Nhiều byte string đệm'],
['Nhiều byte string đệm', 0, '充', STR_PAD_RIGHT, 'Nhiều byte string đệm'],
['Nhiều byte string đệm', 0, '煻充', STR_PAD_BOTH, 'Nhiều byte string đệm'],
['Nhiều byte string đệm', 20, ' ', STR_PAD_BOTH, 'Nhiều byte string đệm'],
['Nhiều byte string đệm', 20, ' ', STR_PAD_LEFT, 'Nhiều byte string đệm'],
['Nhiều byte string đệm', 20, ' ', STR_PAD_RIGHT, 'Nhiều byte string đệm'],
['Nhiều byte string đệm', 20, '充', STR_PAD_BOTH, 'Nhiều byte string đệm'],
['Nhiều byte string đệm', 20, '充', STR_PAD_LEFT, 'Nhiều byte string đệm'],
['Nhiều byte string đệm', 20, '充', STR_PAD_RIGHT, 'Nhiều byte string đệm'],
['Nhiều byte string đệm', 20, '煻充', STR_PAD_BOTH, 'Nhiều byte string đệm'],
['Nhiều byte string đệm', 21, ' ', STR_PAD_BOTH, 'Nhiều byte string đệm'],
['Nhiều byte string đệm', 21, ' ', STR_PAD_LEFT, 'Nhiều byte string đệm'],
['Nhiều byte string đệm', 21, ' ', STR_PAD_RIGHT, 'Nhiều byte string đệm'],
['Nhiều byte string đệm', 21, '充', STR_PAD_BOTH, 'Nhiều byte string đệm'],
['Nhiều byte string đệm', 21, '充', STR_PAD_LEFT, 'Nhiều byte string đệm'],
['Nhiều byte string đệm', 21, '充', STR_PAD_RIGHT, 'Nhiều byte string đệm'],
['Nhiều byte string đệm', 21, '煻充', STR_PAD_BOTH, 'Nhiều byte string đệm'],
['Nhiều byte string đệm', 22, ' ', STR_PAD_BOTH, 'Nhiều byte string đệm '],
['Nhiều byte string đệm', 22, ' ', STR_PAD_LEFT, ' Nhiều byte string đệm'],
['Nhiều byte string đệm', 22, ' ', STR_PAD_RIGHT, 'Nhiều byte string đệm '],
['Nhiều byte string đệm', 22, '充', STR_PAD_BOTH, 'Nhiều byte string đệm充'],
['Nhiều byte string đệm', 22, '充', STR_PAD_LEFT, '充Nhiều byte string đệm'],
['Nhiều byte string đệm', 22, '充', STR_PAD_RIGHT, 'Nhiều byte string đệm充'],
['Nhiều byte string đệm', 22, '煻充', STR_PAD_BOTH, 'Nhiều byte string đệm煻'],
['Nhiều byte string đệm', 23, ' ', STR_PAD_BOTH, ' Nhiều byte string đệm '],
['Nhiều byte string đệm', 23, ' ', STR_PAD_LEFT, ' Nhiều byte string đệm'],
['Nhiều byte string đệm', 23, ' ', STR_PAD_RIGHT, 'Nhiều byte string đệm '],
['Nhiều byte string đệm', 23, '充', STR_PAD_BOTH, '充Nhiều byte string đệm充'],
['Nhiều byte string đệm', 23, '充', STR_PAD_LEFT, '充充Nhiều byte string đệm'],
['Nhiều byte string đệm', 23, '充', STR_PAD_RIGHT, 'Nhiều byte string đệm充充'],
['Nhiều byte string đệm', 23, '煻充', STR_PAD_BOTH, '煻Nhiều byte string đệm煻'],
['Nhiều byte string đệm', 24, ' ', STR_PAD_BOTH, ' Nhiều byte string đệm '],
['Nhiều byte string đệm', 24, ' ', STR_PAD_LEFT, ' Nhiều byte string đệm'],
['Nhiều byte string đệm', 24, ' ', STR_PAD_RIGHT, 'Nhiều byte string đệm '],
['Nhiều byte string đệm', 24, '充', STR_PAD_BOTH, '充Nhiều byte string đệm充充'],
['Nhiều byte string đệm', 24, '充', STR_PAD_LEFT, '充充充Nhiều byte string đệm'],
['Nhiều byte string đệm', 24, '充', STR_PAD_RIGHT, 'Nhiều byte string đệm充充充'],
['Nhiều byte string đệm', 24, '煻充', STR_PAD_BOTH, '煻Nhiều byte string đệm煻充'],
['Nhiều byte string đệm', 25, '煻充', STR_PAD_BOTH, '煻充Nhiều byte string đệm煻充'],
['Nhiều byte string đệm', 26, '煻充', STR_PAD_BOTH, '煻充Nhiều byte string đệm煻充煻'],
];
}
Solution 5
Notice: This is not exact answer to the original question from user2032610. But it will help in situatuation, if you don't need to fill up string with unicode character(s), but with spaces, dots, etc. (I put it here because I was searching for solution and couldn't find some simple one. It may help others in similar situation.)
- Simply use mb_strlen() to calculate real string length.
- Then echo the $str followed by str_pad pattern (dots, in this case).
$str = "nü";
echo "nü" . str_pad('', 5 - mb_strlen($str), ".");
result: nü...
Related videos on Youtube
Admin
Updated on June 11, 2022Comments
-
Admin almost 2 years
I'm just trying to make fixed the
$str
to 5 characters, but couldn't.$str = "nü"; echo str_pad($str, 5, "ü"); // give nüü
I know that's an unicode issue and searched a lot but no luck. I tried somethings such as;
echo str_pad($str, 4 + mb_strlen($s), $s); echo str_pad($str, 5 + mb_strlen($s), $s);
Also I tried this http://www.php.net/manual/de/function.str-pad.php#89754 and saw this https://stackoverflow.com/a/11871948/362780.
Any experience on this issue?
Thanks.
-
K-Gun over 11 yearsI tested your code and
mb_str_pad("...", 4, "ÄÖ")
gives...ÄÖ
, butstr_pad_unicode("...", 4, "ÄÖ")
gives...Ä
as expected. -
K-Gun over 11 years@Jack; I did, but this works just for
STR_PAD_RIGHT
: codepad.viper-7.com/eXnyld -
Ja͢ck over 11 years@Antony It was because pad string had more than one character; worked around it, but it would still break for a pad of
aÄ
. -
Ja͢ck over 11 years@qeremy Yeah, I was hoping of keeping it short, but it seems that it's just not stable unless completely worked out :( at least for pads of more than one character.
-
K-Gun over 11 years@Jack No man, your code works well but when pad is "both" it fails, and my code fails when pad is "left" (God! This is PHP). I'm updating my answer for test codes.
-
beerwin over 11 yearsIt would be fair to put a comment here after downvoting (if it was because i didn't indicate the source, sorry and thanks for placing it for me).
-
Ja͢ck over 11 yearsI didn't downvote you, though perhaps it was downvoted because the code doesn't actually work.
-
user23127 about 10 yearsShould
$repeat = ceil($str_len - $pad_str_len + $pad_len);
not be$repeat = ceil(($str_len - $pad_len)/$pad_str_len);
? Your version works but is somewhat inefficient. -
Wes over 9 yearsthis looks inefficient indeed :P I wrote a simpler one. check it out, I just posted it
-
YesThatIsMyName about 5 yearsPlease add some comments/descriptions to your answer to improve it.