strtok

(PHP 4, PHP 5, PHP 7, PHP 8)

strtok将字符串分解为标记

描述

strtok(string $string, string $token): string|false

备用签名(不支持命名参数)

strtok(string $token): string|false

strtok() 将字符串 (string) 分解成较小的字符串(标记),每个标记由 token 中的任何字符分隔。也就是说,如果您有一个像 "This is an example string" 这样的字符串,您可以使用空格字符作为 token 将此字符串分解成各个单词。

请注意,只有第一次调用 strtok 使用 string 参数。后续每次调用 strtok 只需要使用 token,因为它会跟踪它在当前字符串中的位置。要重新开始,或对新字符串进行标记,只需再次调用 strtok 并传入 string 参数来初始化它。请注意,您可以在 token 参数中放入多个标记。当找到 token 参数中的任何一个字符时,字符串将被标记化。

注意:

此函数的行为与熟悉 explode() 的人可能期望的略有不同。首先,解析字符串中两个或多个连续的 token 字符被视为单个分隔符。此外,位于字符串开头或结尾的 token 将被忽略。例如,如果使用字符串 ";aaa;;bbb;",则连续调用 strtok() 并将 ";" 作为 token 将返回字符串 "aaa" 和 "bbb",然后是 false。因此,字符串将只被分成两个元素,而 explode(";", $string) 将返回一个包含 5 个元素的数组。

参数

string

要分解成较小字符串(标记)的 string

token

分解 string 时使用的分隔符。

返回值

一个 string 标记,或者如果没有更多标记可用,则为 false

变更日志

版本 描述
8.3.0 现在在未提供 token 时发出 E_WARNING

范例

范例 #1 strtok() 例子

<?php
$string
= "This is\tan example\nstring";
/* 使用制表符和换行符作为标记化字符 */
$tok = strtok($string, " \n\t");

while (
$tok !== false) {
echo
"Word=$tok<br />";
$tok = strtok(" \n\t");
}
?>

范例 #2 strtok() 在找到空部分时的行为

<?php
$first_token
= strtok('/something', '/');
$second_token = strtok('/');
var_dump($first_token, $second_token);
?>

上面的例子将输出

string(9) "something"
    bool(false)

范例 #3 strtok()explode() 之间的区别

<?php
$string
= ";aaa;;bbb;";

$parts = [];
$tok = strtok($string, ";");
while (
$tok !== false) {
$parts[] = $tok;
$tok = strtok(";");
}
echo
json_encode($parts),"\n";

$parts = explode(";", $string);
echo
json_encode($parts),"\n";

上面的例子将输出

["aaa","bbb"]
["","aaa","","bbb",""]

注释

警告

此函数可能返回布尔值 false,但也可能返回一个非布尔值,该值计算结果为 false。请阅读关于 布尔值 的部分以了解更多信息。使用 === 运算符 测试此函数的返回值。

参见

添加注释

用户贡献的注释 21 条注释

eep2004 at ukr dot net
10 年前
<?php
// strtok 例子
$str = 'Hello to all of Ukraine';
echo
strtok($str, ' ').' '.strtok(' ').' '.strtok(' ');
?>
结果
Hello to all
manicdepressive at mindless dot com
20 年前
<pre><?php
/** 获取前导、后缀和嵌入式分隔符标记,这些标记在您使用 php 实现一个简单的解析器时被“跳过”
需要在构建解析树时检测嵌套子句 */

$str = "(((alpha(beta))(gamma))";

$seps = '()';
$tok = strtok( $str,$seps ); // 在空字符串或 null 上返回 false
$cur = 0;
$dumbDone = FALSE;
$done = (FALSE===$tok);
while (!
$done) {
// 处理跳过的标记(如果有的话,在第一次迭代时)(最后一次迭代时特殊处理)
$posTok = $dumbDone ? strlen($str) : strpos($str, $tok, $cur );
$skippedMany = substr( $str, $cur, $posTok-$cur ); // 当宽度为 0 时为 false
$lenSkipped = strlen($skippedMany); // 当为 false 时为 0
if (0!==$lenSkipped) {
$last = strlen($skippedMany) -1;
for(
$i=0; $i<=$last; $i++){
$skipped = $skippedMany[$i];
$cur += strlen($skipped);
echo
"skipped: $skipped\n";
}
}
if (
$dumbDone) break; // 这是循环终止的唯一地方

// 处理当前标记
echo "curr tok: ".$tok."\n";

// 更新游标
$cur += strlen($tok);

// 获取下一个标记
if (!$dumbDone){
$tok = strtok($seps);
$dumbDone = (FALSE===$tok);
// 直到检查了尾部跳过的标记,才算真正完成
}
};
?></pre>
elarlang at gmail dot com
13 年前
如果你有内存使用量很重要的解决方案,你应该记住,strtok 函数在使用后会将输入字符串参数(或指向它的引用?)保存在内存中。

<?php
function tokenize($str, $token_symbols) {
$word = strtok($str, $token_symbols);
while (
false !== $word) {
// 在这里做一些事情...
$word = strtok($token_symbols);
}
}
?>
测试用例,处理约 10MB 纯文本文件
案例 #1 - unset $str 变量
<?php
$token_symbols
= " \t\n";
$str = file_get_contents('10MB.txt'); // 内存使用量 9.75383758545 MB (memory_get_usage() / 1024 / 1024));
tokenize($str, $token_symbols); // 内存使用量 9.75400161743 MB
unset($str); // 9.75395584106 MB
?>
案例 #1 结果:内存仍在使用中

案例 #2 - 再次调用 strtok
<?php
$token_symbols
= " \t\n";
$str = file_get_contents('10MB.txt'); // 9.75401306152 MB
tokenize($str, $token_symbols); // 9.75417709351
strtok('', ''); // 9.75421524048
?>
案例 #2 结果:内存仍在使用中

案例 #3 - 再次调用 strtok 并且 unset $str 变量
<?php
$token_symbols
= " \t\n";
$str = file_get_contents('10MB.txt'); // 9.75410079956 MB
tokenize($str, $token_symbols); // 9.75426483154 MB
unset($str);
strtok('', ''); // 0.0543975830078 MB
?>
案例 #3 结果:内存已释放

所以,更好的 tokenize 函数解决方案
<?php
function tokenize($str, $token_symbols, $token_reset = true) {
$word = strtok($str, $token_symbols);
while (
false !== $word) {
// 在这里做一些事情...
$word = strtok($token_symbols);
}

if(
$token_reset)
strtok('', '');
}
?>
eep2004 at ukr dot net
9 年前
从 URL 中移除 GET 变量
<?php
echo strtok('http://example.com/index.php?foo=1&bar=2', '?');
?>
结果
http://example.com/index.php
benighted at gmail dot com
14 年前
对搜索参数进行标记的简单方法,包括双引号或单引号引起来的键。如果只找到一个引号,则字符串的剩余部分将被视为该标记的一部分。

<?php
$token
= strtok($keywords,' ');
while (
$token) {
// 查找双引号引起来的标记
if ($token{0}=='"') { $token .= ' '.strtok('"').'"'; }
// 查找单引号引起来的标记
if ($token{0}=="'") { $token .= ' '.strtok("'")."'"; }

$tokens[] = $token;
$token = strtok(' ');
}
?>

如果你想要不带引号的输出结果,可以使用 substr(1,strlen($token)) 并且移除添加尾部引号的部分。
Axeia
10 年前
可能是在指出显而易见的事情,但如果你更愿意使用 for 循环而不是 while 循环(例如,为了保持标记字符串在同一行以提高可读性),那么是可以实现的。另外的好处是,它也不会在循环本身之外创建 $tok 变量。
然而,缺点是,你无法使用 elarlang 提到的技术手动释放使用的内存。

<?php
for($tok = strtok($str, ' _-.'); $tok!==false; $tok = strtok(' _-.'))
{
echo
"$tok </br>";
}
?>
info at maisuma dot jp
10 年前
如果你想只用一个字母进行标记,explode() 比 strtok() 快得多。

<?php
$str
=str_repeat('foo ',10000);

//explode()
$time=microtime(TRUE);
$arr=explode($str,' ');
$time=microtime(TRUE)-$time;
echo
"explode():$time sec.".PHP_EOL;

//strtok()
$time=microtime(TRUE);
$ret=strtok(' ',$str);
while(
$ret!==FALSE){
$ret=strtok(' ');
}
$time=microtime(TRUE)-$time;
echo
"strtok():$time sec.".PHP_EOL;

?>

结果是:(PHP 5.3.3 在 CentOS 上)

explode():0.001317024230957 sec.
strtok():0.0058917999267578 sec.

explode() 在短字符串中大约快五倍。
Logikos
15 年前
这看起来很简单,但我花了很长时间才弄明白,所以我想我会分享它,以防其他人也想要同样的东西

这应该类似于 substr() 但使用令牌而不是字符串!

<?php
/* subtok(string,chr,pos,len)
*
* chr = 用于分隔令牌的字符
* pos = 起始位置
* len = 长度,如果为负数,则从右边开始计数
*
* subtok('a.b.c.d.e','.',0) = 'a.b.c.d.e'
* subtok('a.b.c.d.e','.',0,2) = 'a.b'
* subtok('a.b.c.d.e','.',2,1) = 'c'
* subtok('a.b.c.d.e','.',2,-1) = 'c.d'
* subtok('a.b.c.d.e','.',-4) = 'b.c.d.e'
* subtok('a.b.c.d.e','.',-4,2) = 'b.c'
* subtok('a.b.c.d.e','.',-4,-1) = 'b.c.d'
*/
function subtok($string,$chr,$pos,$len = NULL) {
return
implode($chr,array_slice(explode($chr,$string),$pos,$len));
}
?>

explode 将令牌分解成数组,array slice 允许您选择您想要的令牌,然后 implode 将其转换回字符串

虽然它远非克隆,但这受到 mIRC 的 gettok() 函数的启发
gilthans at NOSPAM dot gmail dot com
12 年前
请注意,strtok 每次可能接收不同的令牌。因此,例如,如果您希望提取几个词,然后提取句子的其余部分

<?php
$text
= "13 202 5 This is a long message explaining the error codes.";
$error1 = strtok($text, " "); //13
$error2 = strtok(" "); //202
$error3 = strtok(" "); //5
$error_message = strtok(""); //注意不同的令牌参数
echo $error_message; //This is a long message explaining the error codes.
?>
KrazyBox
15 年前
从 strtok() 处理空字符串的方式改变开始,它现在对依赖空数据运行的脚本毫无用处。

例如,标准标题。(使用 UNIX 换行符)

http/1.0 200 OK\n
Content-Type: text/html\n
\n
--HTML BODY HERE---

使用 strtok 解析时,人们会等到它找到一个空字符串来指示标题的结束。但是,由于 strtok 现在跳过空段,因此无法知道标题何时结束。
这不能称为“正确”的行为,它当然不是。它使 strtok 无法(正确地)处理一个非常简单的标准。

但是,这种新功能不影响 Windows 风格的标题。您将搜索仅包含“\r”的行
但是,这不能成为更改的理由。
azeem
14 年前
这是一个使用 strtok 函数的类似 Java 的 StringTokenizer 类

<?php

/**
* 字符串令牌化器类允许应用程序将字符串分解为令牌。
*
* @example 以下是令牌化器使用的一个示例。代码:
* <code>
* <?php
* $str = 'this is:@\t\n a test!';
* $delim = ' !@:'\t\n; // 删除这些字符
* $st = new StringTokenizer($str, $delim);
* while ($st->hasMoreTokens()) {
* echo $st->nextToken() . "\n";
* }
* 打印以下输出:
* this
* is
* a
* test
* ?>
* </code>
*/
class StringTokenizer {

/**
* @var string
*/
private $token;

/**
* @var string
*/
private $delim;
/**
* 为指定的字符串构造一个字符串令牌化器
* @param string $str 要令牌化的字符串
* @param string $delim 分隔令牌的定界符集(字符),默认值为 ' '
*/
public function __construct(/*string*/ $str, /*string*/ $delim = ' ') {
$this->token = strtok($str, $delim);
$this->delim = $delim;
}

public function
__destruct() {
unset(
$this);
}

/**
* 测试此令牌化器字符串中是否有更多令牌可用。它
* 不会以任何方式移动内部指针。要将内部指针
* 移动到下一个元素,请调用 nextToken()
* @return boolean - 如果有更多令牌,则为 true,否则为 false
*/
public function hasMoreTokens() {
return (
$this->token !== false);
}

/**
* 从此字符串令牌化器返回下一个令牌,并将内部
* 指针向前移动一个位置。
* @return string - 令牌化字符串中的下一个元素
*/
public function nextToken() {
$current = $this->token;
$this->token = strtok($this->delim);
return
$current;
}
}
?>
voojj3054 at gmail dot com
1 年前
您好,strtok 的葡萄牙语文档是错误的,在示例 (2) 错误的这一部分。

示例 #2 strtok() 的旧行为
<?php
$first_token
= strtok('/something', '/');
$second_token = strtok('/');
var_dump ($first_token, $second_token);
?>

上面的示例将产生

string(0) ""
string(9) "something"

(上面的这个示例应该反过来,如下所示:)

正确
string(9) "something"
string(0) ""

(示例 3 是正确的)
示例 #3 strtok() 的新行为
<?php
$first_token
= strtok('/something', '/');
$second_token = strtok('/');
var_dump ($first_token, $second_token);
?>

上面的示例将产生

string(9) "something"
bool(false)
bohwaz
8 个月前
请注意,strtok 内存由当前执行的所有 PHP 代码共享,甚至包括包含的文件。如果您不小心,这会以意想不到的方式影响您。

例如

<?php

$path
= 'dir/file.ext';
$dir_name = strtok($path, '/');

if (
$dir_name !== (new Module)->getAllowedDirName()) {
throw new
\Exception('Invalid directory name');
}

$file_name = strtok('');

?>

看起来很简单,但如果您的 Module 类没有加载,这会触发自动加载器。自动加载器 *可能* 在其加载代码中使用 strtok。

或者您的 Module 类 *可能* 在其构造函数中使用 strtok。

这意味着您永远无法正确获取 $file_name。

所以:您应该 *始终* 将 strtok 调用分组,在两次 strtok 调用之间没有任何外部代码。

这将是可以的

<?php

$path
= 'dir/file.ext';
$dir_name = strtok($path, '/');
$file_name = strtok('');

if (
$dir_name !== (new Module)->getAllowedDirName()) {
throw new
\Exception('Invalid directory name');
}

?>

这可能会导致问题

<?php

$path
= 'one/two#three';
$a = strtok($path, '/');
$b = strtok(Module::NAME_SEPARATOR);
$c = strtok('');

?>

因为你的自动加载器可能正在使用 strtok。

通过在调用 *之前* 获取 strtok 中使用的所有参数可以避免这种情况

<?php

$path
= 'one/two#three';
$separator = Module::NAME_SEPARATOR;
$a = strtok($path, '/');
$b = strtok($separator);
$c = strtok('');

?>
heiangus at hotmail dot com
5 年前
我发现这对于解析用户在文本字段中输入的链接很有用。

例如,这是一个链接 <http://example.com>

function parselink($link) {
$bit1 = trim(strtok($link, '<'));
$bit2 = trim(strtok('>'));
$html = '<a href="'.$bit2.'">'.$bit1.'</a>';
return $html; // <a href="http://example.com">This is a link</a>
}
David Spector
3 年前
在使用 strtok 调用获得零个或多个标记后,你可以通过使用空字符串作为分隔符调用 strtok 来获得输入字符串的剩余部分。
charlie dot ded at orange dot fr
7 年前
@maisuma 你颠倒了 explode() 和 strtok() 函数的参数,你的代码没有达到预期效果。
你期望逐个标记读取输入字符串,所以等效于 strtok() 的代码是 arra_filter(explode()),因为 explode() 在读取字符串中有多个连续分隔符时会返回空字符串行,例如两个空格之间的。

实际上,如果读取字符串包含多个连续分隔符,strtok() 比 arra_filter(explode()) 快得多(至少快两倍),
如果读取字符串在标记之间只包含一个分隔符,则速度较慢。

<?php

$repeat
= 10;
$delimiter = ':';
$str=str_repeat('foo:',$repeat);

$timeStrtok=microtime(TRUE);
$token = strtok($str, $delimiter);
while(
$token!==FALSE){
//echo $token . ',';
$token=strtok($delimiter);
}
$timeStrtok -=microtime(TRUE);

$timeExplo=microtime(TRUE);
$arr = explode($delimiter, $str);
//$arr = array_filter($arr);
$timeExplo -=microtime(TRUE);

//print_r($arr);

$X = 1000000; $unit = 'microsec';

echo
PHP_EOL . ' explode() : ' . -$timeExplo . ' ' .$unit .PHP_EOL . ' strtok() : ' . -$timeStrtok . ' ' . $unit .PHP_EOL;

$timeExplo=round(-$timeExplo*$X);
$timeStrtok=round(-$timeStrtok*$X);

echo
PHP_EOL . ' explode() : ' . $timeExplo . ' ' .$unit .PHP_EOL . ' strtok() : ' . $timeStrtok . ' ' . $unit .PHP_EOL;
echo
' ratio explode / strtok : ' . round($timeExplo / $timeStrtok,1) . PHP_EOL;

?>
pradador at me dot com
13 年前
这是一个简单的类,允许你使用 foreach 循环遍历字符串标记。

<?php
/**
* TokenIterator 类允许你使用
* 熟悉的 foreach 控制结构遍历字符串标记。
*
* 例子:
* <code>
* <?php
* $string = 'This is a test.';
* $delimiters = ' ';
* $ti = new TokenIterator($string, $delimiters);
*
* foreach ($ti as $count => $token) {
* echo sprintf("%d, %s\n", $count, $token);
* }
*
* // 打印以下输出:
* // 0. This
* // 1. is
* // 2. a
* // 3. test.
* </code>
*/
class TokenIterator implements Iterator
{
/**
* 要标记的字符串。
* @var string
*/
protected $_string;

/**
* 标记分隔符。
* @var string
*/
protected $_delims;

/**
* 存储当前标记。
* @var mixed
*/
protected $_token;

/**
* 内部标记计数器。
* @var int
*/
protected $_counter = 0;

/**
* 构造函数。
*
* @param string $string 要标记的字符串。
* @param string $delims 标记分隔符。
*/
public function __construct($string, $delims)
{
$this->_string = $string;
$this->_delims = $delims;
$this->_token = strtok($string, $delims);
}

/**
* @see Iterator::current()
*/
public function current()
{
return
$this->_token;
}

/**
* @see Iterator::key()
*/
public function key()
{
return
$this->_counter;
}

/**
* @see Iterator::next()
*/
public function next()
{
$this->_token = strtok($this->_delims);

if (
$this->valid()) {
++
$this->_counter;
}
}

/**
* @see Iterator::rewind()
*/
public function rewind()
{
$this->_counter = 0;
$this->_token = strtok($this->_string, $this->_delims);
}

/**
* @see Iterator::valid()
*/
public function valid()
{
return
$this->_token !== FALSE;
}
}
?>
mac.com@nemo
18 年前
此函数接受一个字符串,并返回一个包含单词(以空格分隔)的数组,同时还考虑引号、双引号、反引号和反斜杠(用于转义内容)。
所以

$string = "cp 'my file' to `Judy's file`";
var_dump(parse_cli($string));

将产生

array(4) {
[0]=>
string(2) "cp"
[1]=>
string(7) "my file"
[2]=>
string(5) "to"
[3]=>
string(11) "Judy's file"
}

它的工作方式是逐个字符地遍历字符串,并根据每个字符及其当前状态 $state 来查找要执行的操作。
操作可以是(一个或多个)将字符/字符串添加到当前单词,将单词添加到输出数组,以及更改或(重新)存储状态。
例如,如果 $state 为 'doublequoted',则空格将成为当前 'word'(或 'token')的一部分,但如果 $state 为 'unquoted',则空格将开始一个新的 token。
后来我被告知这是一个“使用有限状态自动机的词法分析器”。谁知道呢 :-)

<?php

#_____________________
# parse_cli($string) /
function parse_cli($string) {
$state = 'space';
$previous = ''; // 存储遇到反斜杠时的当前状态(它将 $state 更改为 'escaped',但随后必须回退到上一个 $state)
$out = array(); // 返回值
$word = '';
$type = ''; // 字符类型
// array[states][chartypes] => actions
$chart = array(
'space' => array('space'=>'', 'quote'=>'q', 'doublequote'=>'d', 'backtick'=>'b', 'backslash'=>'ue', 'other'=>'ua'),
'unquoted' => array('space'=>'w ', 'quote'=>'a', 'doublequote'=>'a', 'backtick'=>'a', 'backslash'=>'e', 'other'=>'a'),
'quoted' => array('space'=>'a', 'quote'=>'w ', 'doublequote'=>'a', 'backtick'=>'a', 'backslash'=>'e', 'other'=>'a'),
'doublequoted' => array('space'=>'a', 'quote'=>'a', 'doublequote'=>'w ', 'backtick'=>'a', 'backslash'=>'e', 'other'=>'a'),
'backticked' => array('space'=>'a', 'quote'=>'a', 'doublequote'=>'a', 'backtick'=>'w ', 'backslash'=>'e', 'other'=>'a'),
'escaped' => array('space'=>'ap', 'quote'=>'ap', 'doublequote'=>'ap', 'backtick'=>'ap', 'backslash'=>'ap', 'other'=>'ap'));
for (
$i=0; $i<=strlen($string); $i++) {
$char = substr($string, $i, 1);
$type = array_search($char, array('space'=>' ', 'quote'=>'\'', 'doublequote'=>'"', 'backtick'=>'`', 'backslash'=>'\\'));
if (!
$type) $type = 'other';
if (
$type == 'other') {
// 一次性获取所有紧随当前字符的也是 'other' 的字符
preg_match("/[ \'\"\`\\\]/", $string, $matches, PREG_OFFSET_CAPTURE, $i);
if (
$matches) {
$matches = $matches[0];
$char = substr($string, $i, $matches[1]-$i); // 是的,$char 长度可以大于 1
$i = $matches[1] - 1;
}else{
// 在特殊字符上不再匹配,这意味着这必须是最后一个单词!
// 以下的 .= 是因为我们可能处于一个刚刚包含特殊字符的单词的中间位置
$word .= substr($string, $i);
break;
// 跳出 for() 循环
}
}
$actions = $chart[$state][$type];
for(
$j=0; $j<strlen($actions); $j++) {
$act = substr($actions, $j, 1);
if (
$act == ' ') $state = 'space';
if (
$act == 'u') $state = 'unquoted';
if (
$act == 'q') $state = 'quoted';
if (
$act == 'd') $state = 'doublequoted';
if (
$act == 'b') $state = 'backticked';
if (
$act == 'e') { $previous = $state; $state = 'escaped'; }
if (
$act == 'a') $word .= $char;
if (
$act == 'w') { $out[] = $word; $word = ''; }
if (
$act == 'p') $state = $previous;
}
}
if (
strlen($word)) $out[] = $word;
return
$out;
}

?>
fabiolimasouto at gmail dot com
13 年前
这个例子希望能帮助你理解这个函数是如何工作的

<?php
$selector
= 'div.class#id';
$tagname = strtok($selector,'.#');
echo
$tagname.'<br/>';

while(
$tok = strtok('.#'))
{
echo
$tok.'<br/>';
}

?>

输出
div
class
id
yanick dot rochon at gmail dot com
15 年前
以下是我写的一个小函数,因为我需要从字符串中提取一些命名的 token(类似于 Google)。例如,我需要将像 "extension:gif size:64M animated:true author:'John Bash'" 这样的字符串格式化为

array(
'extension' => 'gif',
'size' => '64M',
'animated' => true,
'author' => 'John Bash'
)

所以,这是代码

<?php

header
('Content-type: text/plain; charset=utf-8');

/**
* 注意:使用 mbstring.func_overload 对该函数的 多字节支持
*
* @param string $string 要标记化的字符串
* @param int $offset 起始偏移量
* @param string $defaultTokenName 如果没有指定,则为默认标记名称
* @param string $groupDelimiters 标记组的定界符
* @param string $groupNameDelimiter 标记组名称的定界符
* @return array
*/
function getTokens(
$string,
$offset = 0,
$defaultTokenName = null,
$groupDelimiters = '\'"',
$groupNameDelimiter = ':')
{

if (
$offset >= strlen($string)) {
//echo "offset out of range";
return false;
}

$spaces = " \t\n\r"; // 空格字符

// 将组定界符添加到空格中...
$groupSpaces = $spaces . $groupNameDelimiter;
$delimiters = $groupSpaces . $groupDelimiters;

//var_dump($groupSpaces);

$string = ltrim(substr($string, $offset), $groupSpaces);
$token_strings = array();

//echo "String is : " . $string . "\n";

// 1. 拆分所有标记...
while ($offset < strlen($string)) {
$lastOffset = $offset;
$escaped = false;

if (
false !== strpos($groupDelimiters, $char = $string[$offset])) {
$groupChar = $char;
} else {
$groupChar = null;
}

if (
null !== $groupChar) {
while ((
$offset < strlen($string)) && (($groupChar !== ($char = $string[++$offset])) || $escaped)) {
//$offset++;
$escaped = ('\\' === $char);
}
$offset++;
//echo "*** Grouped : " . substr($string, $lastOffset, $offset - $lastOffset) . "\n";
} else {
while ((
$offset < strlen($string)) && ((false === strpos($delimiters, $char = $string[$offset])) || $escaped)) {
$offset++;
$escaped = ('\\' === $char);
}
//echo "*** Non-group : " . substr($string, $lastOffset, $offset - $lastOffset) . "\n";
}
//跳过空格...
while (($offset < strlen($string)) && ((false !== strpos($groupSpaces, $char = $string[$offset])) || $escaped)) {
$offset++;
$escaped = ('\\' === $char);
}

$token_strings[] = substr($string, $lastOffset, $offset - $lastOffset);
//echo "Next token = '" . end($token_strings) . "'\n";
}

$tokens = array();
$tokenName = null;
foreach (
$token_strings as $token_str) {
// 清理 $token_str
$token_str = trim(stripslashes($token_str), $spaces);
$str_value = trim($token_str, $delimiters);
switch (
strtolower($str_value)) {
case
'true': $str_value = true; break;
case
'false': $str_value = false; break;
default: break;
}

// 它是一个标记名称吗?
if (':' === substr($token_str, -1, 1)) {
if (!empty(
$tokenName)) {
$tokens[$tokenName] = '';
}
$tokenName = trim($token_str, $delimiters);
} else {
if (!empty(
$tokenName)) {
if (isset(
$tokens[$tokenName])) {
$tokens[$tokenName] = array(
$tokens[$tokenName],
$str_value
);
} else {
$tokens[$tokenName] = $str_value;
}
$tokenName = null;
} elseif (empty(
$defaultTokenName)) {
$tokens[] = trim($token_str, $delimiters);;
} else {
if (isset(
$tokens[$defaultTokenName])) {
$tokens[$defaultTokenName] = array(
$tokens[$defaultTokenName],
$str_value
);
} else {
$tokens[$defaultTokenName] = $str_value;
}
}
}
}
if (!empty(
$tokenName)) {
$tokens[$tokenName] = '';
}

return
$tokens;
}

$str = "check1: test "
. "check2:'hello world' "
. 'check3: "foo" '
. "check4: \\\"try this\\\""
. '"buz" '
. 'check1:true';

?>
To Top