PHP的strlen()和mb_strlen()返回意外结果

3
使用PHP,我正在尝试确定这些字符串中的长度(字符数量):
1
1.1
1.1.1
1.1.2
1.1.3
1.1.3.1
1.1.3.2
1.1.4
1.1.5
1.1.6
1.1.7

当使用mb_strlen()或strlen()测量这些字符串的长度时,结果会是:

------------------------------
value   | mb_strlen() | strlen()
------------------------------
1       | 1           | 1
------------------------------
1.1     | 5           | 5
------------------------------
1.1.1   | 9           | 9
------------------------------
1.1.1.1 | 13          | 13
------------------------------
1.1.1.2 | 13          | 13
------------------------------
1.1.1.3 | 13          | 13
------------------------------

看起来它把“.”算作了3个字符?我想只需编写一个小函数来补偿可预测的“计数错误”,但是我想知道为什么一开始就将“。”算作3个字符。
我已经查看了几个地方,包括此SO文章阅读所提到的文章,并在页面中添加了建议的转换。
mb_language('uni');
mb_internal_encoding('UTF-8');
$str = mb_convert_encoding($str, 'UTF-8', 'UTF-8');

出了什么问题?

编辑: 这些字符串是作为csv导入的一部分导入的。

以下是代码:

<?
    $f = fopen("s2db.csv", "r");
    while (($line = fgetcsv($f)) !== false) {

            $colcount = 0;
            foreach ($line as $cell) {
                //lets get the lines into variables first
                //there only five, so just count
                switch ($colcount) {
                    case '0':
                        $item = $cell;
                        break;
                    case '1':
                        $itemtitle = htmlspecialchars($cell);
                        break;
                    case '2':
                        $itemsubject = htmlspecialchars($cell);
                        break;
                    case '3':
                        $itemnumber = htmlspecialchars($cell);
                        break;
                    case '4':
                        $itemqty = htmlspecialchars($cell);
                        break;
                    case '5':
                        $itemfilename = htmlspecialchars($cell);
                        break;                    
                }
                $colcount++;
            }
            $itemlen = strlen($item);
            echo "Value = " . $item . " | strlen() Length = " . $itemlen .  "|  mb_strlen() = " . mb_strlen($item) . "</br>";
    }
?>

以下是结果

Value = 1 | strlen() Length = 3| mb_strlen() = 3
Value = 1.1 | strlen() Length = 7| mb_strlen() = 7
Value = 1.1.1 | strlen() Length = 11| mb_strlen() = 11
Value = 1.1.1.1 | strlen() Length = 15| mb_strlen() = 15
Value = 1.1.1.2 | strlen() Length = 15| mb_strlen() = 15
Value = 1.1.1.3 | strlen() Length = 15| mb_strlen() = 15
Value = 1.1.1.3.1 | strlen() Length = 19| mb_strlen() = 19
Value = 1.1.1.3.2 | strlen() Length = 19| mb_strlen() = 19
Value = 1.1.1.3.3 | strlen() Length = 19| mb_strlen() = 19
Value = 1.1.1.4 | strlen() Length = 15| mb_strlen() = 15

解决方案:

我选择投票给@hek2mgl,因为他的十六进制转储帮助我确定我不是疯了,它确实将“.” 计算为 3,如此所示。

我无法改变导入格式,所以我只能添加代码来进行补偿:

感谢大家的帮助!


1
你的点号有什么字符编码?php -r 'echo ord(".");' 的输出是什么? - Viacheslav Kondratiuk
1
您提供的代码似乎不太可用 - 您没有引用字符串,因此它们被视为数字,因此无效。修复这个问题可能会很有价值... - Rob Baillie
请明确一点,您的代码的输出strlen(1.1.1.3.3),但实际上它使用字符串参数调用这些函数? - user395760
2
我无法重现你的问题:http://ideone.com/W3dF8t - ComFreek
检查CSV文件的编码。 - Tivie
显示剩余5条评论
3个回答

3

我理解为:

<?php

$str = '1.1.1';
var_dump(mb_strlen($str, 'utf-8'));  // 5
var_dump(strlen($str));              // 5

预料之中。在你的情况下,似乎.不是常规的点,而是一个特殊的unicode字符。请展示输入数据的十六进制转储。您可以使用Hexdump进行此操作(我编写了该软件包以用于这种情况): 安装
sudo pear channel-discover www.metashock.de/pear
sudo pear install metashock/Hexdump

用法:

<?php

require_once 'Hexdump.php';
hexdump('1.1.1');

很有趣的是能够看到幕后真正的人物。


首先:这是一个很棒的小包!以下是结果链接:http://pastebin.com/Barz1Y5P# - Edward
是的,那就是我想要的输出!! :).. 你看到每个 . 前面都有一个 0 字节。你从哪里得到这些字符串的? - hek2mgl
我无法控制要导入的csv文件,但知道每次都是相同的。有没有办法确定它的编码,以便mb_strlen()可以正确读取它? - Edward
@Edward 这不是一个真正的编码方式。我以前从未见过这种方式。你能展示完整的csv文件吗?(在pastebin上?) - hek2mgl
@Edward 对我来说,看起来也没问题。 - hek2mgl
显示剩余2条评论

0

我知道这不是一个答案,但出于代码格式化的原因。

以下内容保存在我的设置中的UTF-8文件中...

<?php

echo 'mbstring.internal_encoding: '    . ini_get( 'mbstring.internal_encoding' ) . "\r\n";
echo 'mbstring.func_overload: '        . ini_get( 'mbstring.func_overload' ) . "\r\n";
echo 'mbstring.language: '             . ini_get( 'mbstring.language' ) . "\r\n";
echo 'mbstring.strict_detection: '     . ini_get( 'mbstring.strict_detection' ) . "\r\n";
echo 'mbstring.substitute_character: ' . ini_get( 'mbstring.substitute_character' ) . "\r\n";
echo 'mbstring.detect_order: '         . ini_get( 'mbstring.detect_order' ) . "\r\n";
echo 'mbstring.encoding_translation: ' . ini_get( 'mbstring.encoding_translation' ) . "\r\n";
echo "\r\n";

function outputLengths( $sString )  {
    echo( "mb_strlen('$sString', 'utf-8') = " . mb_strlen($sString, 'utf-8')  ."\r\n" );
    echo( "strlen('$sString') = " . strlen($sString)  ."\r\n\r\n" );
}

outputLengths( '1' );
outputLengths( '1.1' );
outputLengths( '1.1.1' );
outputLengths( '1.1.3.1' );

输出:

mbstring.internal_encoding: UTF-8
mbstring.func_overload: 0
mbstring.language: neutral
mbstring.strict_detection: 0
mbstring.substitute_character:
mbstring.detect_order:
mbstring.encoding_translation: 0

mb_strlen('1', 'utf-8') = 1
strlen('1') = 1

mb_strlen('1.1', 'utf-8') = 3
strlen('1.1') = 3

mb_strlen('1.1.1', 'utf-8') = 5
strlen('1.1.1') = 5

mb_strlen('1.1.3.1', 'utf-8') = 7
strlen('1.1.3.1') = 7

你会得到什么?

将你的代码复制粘贴到我的系统中,我得到的结果与你展示的相同。 - Edward
听起来你和 @hek2mglm 的想法是一致的,测试只是帮助确认答案所述的 - 这是一个数据问题。所以我会离开这个线程。 - Rob Baillie

0

你的变量是从哪里来的? 能否请您展示真实的代码(而不是伪代码)?

我尝试复现所描述的行为,但未能成功。以下是我进行的一些测试:

$strArray = array(
    '.',
    '1',
    '1.1',
    '1.1.1',
    1,
    1.1,
);

for ($i = 0; $i<count($strArray); ++$i) {
    print "{$strArray[$i]} -> strlen: ".strlen($strArray[$i])." <br/>";
    print "{$strArray[$i]} -> mb_strlen: ".mb_strlen($strArray[$i])." <br/>";
    print '<br>';  
}

这将输出:

. -> strlen: 1 
. -> mb_strlen: 1 

1 -> strlen: 1 
1 -> mb_strlen: 1 

1.1 -> strlen: 3 
1.1 -> mb_strlen: 3 

1.1.1 -> strlen: 5 
1.1.1 -> mb_strlen: 5 

1 -> strlen: 1 
1 -> mb_strlen: 1 

1.1 -> strlen: 3 
1.1 -> mb_strlen: 3

如预期


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接