文字

strtok

(PHP 4, PHP 5)

strtok标记分割字符串

说明

string strtok ( string $str , string $token )
string strtok ( string $token )

strtok() 将字符串 str 分割为若干子字符串,每个子字符串以 token 中的字符分割。这也就意味着,如果有个字符串是 "This is an example string",你可以使用空格字符将这句话分割成独立的单词。

注意仅第一次调用 strtok 函数时使用 string 参数。后来每次调用 strtok,都将只使用 token 参数,因为它会记住它在字符串 string 中的位置。如果要重新开始分割一个新的字符串,你需要再次使用 string 来调用 strtok 函数,以便完成初始化工作。注意可以在 token 参数中使用多个字符。字符串将被该参数中任何一个字符分割。

参数

str

被分成若干子字符串的原始 字符串

token

分割 str 时使用的分界字符。

返回值

标记后的 字符串

范例

Example #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" );
}
?>

对于空串的处理机制,PHP 从 4.1.0 开始发生了变化。旧的运行机制返回空字符串,而新的运行机制选择恰当地跳过这一部分:

Example #2 旧的 strtok() 运行机制

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

以上例程会输出:

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

Example #3 新的 strtok() 运行机制

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

以上例程会输出:

string(9) "something"
    bool(false)

注释

Warning

此函数可能返回布尔值 FALSE ,但也可能返回等同于 FALSE 的非布尔值。请阅读 布尔类型章节以获取更多信息。应使用 === 运算符来测试此函数的返回值。

参见

  • split() - 用正则表达式将字符串分割到数组中
  • explode() - 使用一个字符串分割另一个字符串

用户评论:

[#1] eep2004 at ukr dot net [2015-01-06 08:40:10]

Remove GET variables from the URL
<?php
echo strtok('http://example.com/index.php?foo=1&bar=2''?');
?>

Result:
http://example.com/index.php

[#2] eep2004 at ukr dot net [2014-10-07 10:01:04]

Remove GET variables from the URL
<?php
$url = strtok('http://php.net/manual/en/ref.strings.php?foo=1&bar=2', '?');
// $url = 'http://php.net/manual/en/ref.strings.php'

[#3] info at maisuma dot jp [2014-05-30 08:15:26]

If you want to tokenize by only one letter, explode() is much faster compared to 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;

?>


The result is : (PHP 5.3.3 on CentOS)

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

explode() is about five times fast in short strings, too.

[#4] Axeia [2014-02-11 17:12:24]

Might be pointing out the obvious but if you'd rather use a for loop rather than a while (to keep the token strings on the same line for readability for example), it can be done. Added bonus, it doesn't put a $tok variable outside the loop itself either.
Downside however is that you're not able to manually free up the memory used using the technique mentioned by elarlang.

<?php
for($tok strtok($str' _-.'); $tok!==false$tok strtok(' _-.'))
{
  echo 
"$tok </br>";
}
?>

[#5] eep2004 at ukr dot net [2013-09-18 16:11:59]

<?php
// strtok example
$str 'Hello to all of Ukraine';
echo 
strtok($str' ').' '.strtok(' ').' '.strtok(' ');
?>

Result:
Hello to all

[#6] gilthans at NOSPAM dot gmail dot com [2012-07-15 18:47:47]

Note that strtok may receive different tokens each time. Therefore, if, for example, you wish to extract several words and then the rest of the sentence:

<?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(""); //Notice the different token parameter
echo $error_message//This is a long message explaining the error codes.
?>

[#7] pradador at me dot com [2011-05-13 12:10:54]

Here's a simple class that allows you to iterate through string tokens using a foreach loop.

<?php

class TokenIterator implements Iterator
{
    

    
protected $_string;
    
    

    
protected $_delims;
    
    

    
protected $_token;
    
    

    
protected $_counter 0;
    
    

    
public function __construct($string$delims)
    {
        
$this->_string $string;
        
$this->_delims $delims;
        
$this->_token strtok($string$delims);
    }
    
    

    
public function current()
    {
        return 
$this->_token;
    }

    

    
public function key()
    {
        return 
$this->_counter;
    }

    

    
public function next()
    {
        
$this->_token strtok($this->_delims);
        
        if (
$this->valid()) {
            ++
$this->_counter;
        }
    }

    

    
public function rewind()
    {
        
$this->_counter 0;
        
$this->_token   strtok($this->_string$this->_delims);
    }

    

    
public function valid()
    {
        return 
$this->_token !== FALSE;
    }
}
?>

[#8] elarlang at gmail dot com [2011-03-22 10:45:01]

If you have memory-usage critical solution, you should keep in mind, that strtok function holds input string parameter (or reference to it?) in memory after usage.

<?php
function tokenize($str$token_symbols) {
    
$word strtok($str$token_symbols);
    while (
false !== $word) {
        
// do something here...
        
$word strtok($token_symbols);
    }
}
?>

Test-cases with handling ~10MB plain-text file:
Case #1 - unset $str variable
<?php
$token_symbols 
" \t\n";
$str file_get_contents('10MB.txt'); // mem usage 9.75383758545 MB (memory_get_usage() / 1024 / 1024)); 
tokenize($str$token_symbols); // mem usage 9.75400161743 MB
unset($str); // 9.75395584106 MB
?>

Case #1 result: memory is still used

Case #2 - call strtok again
<?php
$token_symbols 
" \t\n";
$str file_get_contents('10MB.txt'); // 9.75401306152 MB
tokenize($str$token_symbols); // 9.75417709351
strtok(''''); // 9.75421524048
?>

Case #2 result: memory is still used

Case #3 - call strtok again AND unset $str variable
<?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
?>

Case #3 result: memory is free

So, better solution for tokenize function:
<?php
function tokenize($str$token_symbols$token_reset true) {
    
$word strtok($str$token_symbols);
    while (
false !== $word) {
        
// do something here...
        
$word strtok($token_symbols);
    }

    if(
$token_reset)
        
strtok('''');
}
?>

[#9] fabiolimasouto at gmail dot com [2010-11-20 05:54:36]

this example will hopefully help you understand how this function works:

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

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

?>


Outputs:
div
class
id

[#10] benighted at gmail dot com [2009-11-06 06:47:15]

Simple way to tokenize search parameters, including double or single quoted keys.  If only one quote is found, the rest of the string is assumed to be part of that token.

<?php
            $token 
strtok($keywords,' ');
            while (
$token) {
                
// find double quoted tokens
                
if ($token{0}=='"') { $token .= ' '.strtok('"').'"'; }
                
// find single quoted tokens
                
if ($token{0}=="'") { $token .= ' '.strtok("'")."'"; }

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


Use substr(1,strlen($token)) and remove the part that adds the trailing quotes if you want your output without quotes.

[#11] azeem [2009-09-02 12:14:05]

Here is a java like StringTokenizer class using strtok function:

<?php


class StringTokenizer {

    

    
private $token;

    

    
private $delim;
    

    
public function __construct( $str $delim ' ') {
        
$this->token strtok($str$delim);
        
$this->delim $delim;
    }

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

    

    
public function hasMoreTokens() {
        return (
$this->token !== false);
    }

    

    
public function nextToken() {
        
$current $this->token;
        
$this->token strtok($this->delim);
        return 
$current;
    }
}
?>

[#12] yanick dot rochon at gmail dot com [2009-06-15 09:37:27]

Here is a small function I wrote as I needed to extract some named tokens from a string (a la Google). For example, I needed to format a string like "extension:gif size:64M animated:true author:'John Bash'" into

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

So, here's the code: 

<?php

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


function getTokens(
        
$string
        
$offset 0
        
$defaultTokenName null
        
$groupDelimiters '\'"',
        
$groupNameDelimiter ':'
{

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

    
$spaces " \t\n\r";   // space characters

    // add group delimiters to spaces...
    
$groupSpaces $spaces $groupNameDelimiter;
    
$delimiters $groupSpaces $groupDelimiters;

    
//var_dump($groupSpaces);

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

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

    // 1. split all tokens...
    
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";
        
}
        
//skip spaces...
        
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) {
        
// clean $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;
        }

        
// is it a token name?
        
if (':' === substr($token_str, -11)) {
            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';

?>

[#13] KrazyBox [2009-03-06 18:11:06]

As of the change in strtok()'s handling of empty strings, it is now useless for scripts that rely on empty data to function.

Take for instance, a standard header. (with UNIX newlines)

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

When parsing this with strtok, one would wait until it found an empty string to signal the end of the header. However, because strtok now skips empty segments, it is impossible to know when the header has ended.
This should not be called `correct' behavior, it certainly is not. It has rendered strtok incapable of (properly) processing a very simple standard.

This new functionality, however, does not affect Windows style headers. You would search for a line that only contains "\r"
This, however, is not a justification for the change.

[#14] Logikos [2009-01-03 22:12:02]

This looks very simple, but it took me a long time to figure out so I thought I'd share it incase someone else was wanting the same thing:

this should work similar to substr() but with tokens instead!

<?php

function subtok($string,$chr,$pos,$len NULL) {
  return 
implode($chr,array_slice(explode($chr,$string),$pos,$len));
}
?>


explode breaks the tokens up into an array, array slice alows you to pick then tokens you want, and then implode converts it back to a string

although its far from a clone, this was inspired by mIRC's gettok() function

[#15] mac.com@nemo [2006-02-18 13:49:39]

This function takes a string and returns an array with words (delimited by spaces), also taking into account quotes, doublequotes, backticks and backslashes (for escaping stuff).
So

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

would yield:

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

Way it works, runs through the string character by character, for each character looking up the action to take, based on that character and its current $state.
Actions can be (one or more of) adding the character/string to the current word, adding the word to the output array, and changing or (re)storing the state.
For example a space will become part of the current 'word' (or 'token') if $state is 'doublequoted', but it will start a new token if $state was 'unquoted'.
I was later told it's a "tokeniser using a finite state automaton". Who knew :-)

<?php

#_____________________
# parse_cli($string) /
function parse_cli($string) {
    
$state 'space';
    
$previous '';     // stores current state when encountering a backslash (which changes $state to 'escaped', but has to fall back into the previous $state afterwards)
    
$out = array();     // the return value
    
$word '';
    
$type '';         // type of character
    // 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$i1);
        
$type array_search($char, array('space'=>' ''quote'=>'\'''doublequote'=>'"''backtick'=>'`''backslash'=>'\\'));
        if (! 
$type$type 'other';
        if (
$type == 'other') {
            
// grabs all characters that are also 'other' following the current one in one go
            
preg_match("/[ \'\"\`\\\]/"$string$matchesPREG_OFFSET_CAPTURE$i);
            if (
$matches) {
                
$matches $matches[0];
                
$char substr($string$i$matches[1]-$i); // yep, $char length can be > 1
                
$i $matches[1] - 1;
            }else{
                
// no more match on special characters, that must mean this is the last word!
                // the .= hereunder is because we *might* be in the middle of a word that just contained special chars
                
$word .= substr($string$i);
                break; 
// jumps out of the for() loop
            
}
        }
        
$actions $chart[$state][$type];
        for(
$j=0$j<strlen($actions); $j++) {
            
$act substr($actions$j1);
            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;
}

?>

[#16] manicdepressive at mindless dot com [2004-06-19 06:01:53]

<pre> <?php


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

$seps '()';
$tok strtok$str,$seps ); // return false on empty string or null
$cur 0;      
$dumbDone FALSE;
$done = (FALSE===$tok);
while (!
$done) {
   
// process skipped tokens (if any at first iteration) (special for last)
   
$posTok $dumbDone strlen($str) : strpos($str$tok$cur );
   
$skippedMany substr$str$cur$posTok-$cur ); // false when 0 width
   
$lenSkipped strlen($skippedMany); // 0 when false
   
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; // this is the only place the loop is terminated

   // process current tok
   
echo "curr tok: ".$tok."\n";

   
// update cursor
   
$cur += strlen($tok);

   
// get any next tok
   
if (!$dumbDone){
      
$tok strtok($seps);
      
$dumbDone = (FALSE===$tok); 
      
// you're not really done till you check for trailing skipped
   
}
};
?>
</pre>

上一篇: 下一篇: