strtok

(PHP 4, PHP 5)

strtok — Разбивает строку

Описание

string strtok ( string $arg1 , string $arg2 )

strtok() разбивает строку arg1 на подстроки, используя в качесве разделителей символы из arg2 . Например строку "This is an example string" можно разбить на отдельные слова.

Пример #1 Пример использования strtok()

<?php
$string 
"This is\tan example\nstring";
/* в качестве разделителей используем пробел, табуляцию и перевод строки */
$tok strtok($string" \n\t");
while (
$tok) {
    echo 
"Word=$tok<br />";
    
$tok strtok(" \n\t");
}
?>

Заметьте, что исходная строка передается только при первом вызове этой функции. Последующим вызовам передаются только разделители, так как эта функция сохраняет исходную строку и запоминает позицию в этой строке между вызовами. Для работы с новой строкой нужно снова вызвать функцию с двумя аргументами.

Начиная с PHP 4.1.0, был изменен способ обработки пустых подстрок. До версии 4.1.0 эта функция возвращала пустые подстроки, а начиная с 4.1.0 эти подстроки пропускаются.

Пример #2 Старый способ обработки пустых подстрок функцией strtok()

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

Вывод:

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

Пример #3 Новый способ обработки пустых подстрок функцией strtok()

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

Вывод:

    string(9) "something"
    bool(false)

Помните также о том, что может быть возвращена подстрока "0". В логических выражениях это значение может соответствовать FALSE.

См. также описание функций split() и explode().

Коментарии

<pre><?php
/** get leading, trailing, and embedded separator tokens that were 'skipped'
if for some ungodly reason you are using php to implement a simple parser that 
needs to detect nested clauses as it builds a parse tree */

$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>
2004-06-19 09:01:53
http://php5.kiev.ua/manual/ru/function.strtok.html
Автор:
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;
}

?>
2006-02-18 15:49:39
http://php5.kiev.ua/manual/ru/function.strtok.html
Автор:
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
/* subtok(string,chr,pos,len)
 *
 * chr = chr used to seperate tokens
 * pos = starting postion
 * len = length, if negative count back from right
 * 
 *  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 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
2009-01-04 00:12:02
http://php5.kiev.ua/manual/ru/function.strtok.html
Автор:
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.
2009-03-06 20:11:06
http://php5.kiev.ua/manual/ru/function.strtok.html
Автор:
Here is a java like StringTokenizer class using strtok function:

<?php

/**
 * The string tokenizer class allows an application to break a string into tokens.
 *
 * @example The following is one example of the use of the tokenizer. The code:
 * <code>
 * <?php
 *    $str = 'this is:@\t\n a test!';
 *    $delim = ' !@:'\t\n; // remove these chars
 *    $st = new StringTokenizer($str, $delim);
 *    while ($st->hasMoreTokens()) {
 *        echo $st->nextToken() . "\n";
 *    }
 *    prints the following output:
 *      this
 *      is
 *      a
 *      test
 * ?>
 * </code>
 */
class StringTokenizer {

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

   
/**
     * @var string
     */
   
private $delim;
   
/**
     * Constructs a string tokenizer for the specified string
     * @param string $str String to tokenize
     * @param string $delim The set of delimiters (the characters that separate tokens)
     * specified at creation time, default to ' '
     */
   
public function __construct(/*string*/ $str/*string*/ $delim ' ') {
       
$this->token strtok($str$delim);
       
$this->delim $delim;
    }

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

   
/**
     * Tests if there are more tokens available from this tokenizer's string. It
     * does not move the internal pointer in any way. To move the internal pointer
     * to the next element call nextToken()
     * @return boolean - true if has more tokens, false otherwise
     */
   
public function hasMoreTokens() {
        return (
$this->token !== false);
    }

   
/**
     * Returns the next token from this string tokenizer and advances the internal
     * pointer by one.
     * @return string - next element in the tokenized string
     */
   
public function nextToken() {
       
$current $this->token;
       
$this->token strtok($this->delim);
        return 
$current;
    }
}
?>
2009-09-02 15:14:05
http://php5.kiev.ua/manual/ru/function.strtok.html
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.
2009-11-06 08:47:15
http://php5.kiev.ua/manual/ru/function.strtok.html
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
2010-11-20 07:54:36
http://php5.kiev.ua/manual/ru/function.strtok.html
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('''');
}
?>
2011-03-22 12:45:01
http://php5.kiev.ua/manual/ru/function.strtok.html
Here's a simple class that allows you to iterate through string tokens using a foreach loop.

<?php
/**
 * The TokenIterator class allows you to iterate through string tokens using
 * the familiar foreach control structure.
 * 
 * Example:
 * <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); 
 * }
 * 
 * // Prints the following output:
 * // 0. This
 * // 1. is
 * // 2. a
 * // 3. test.
 * </code>
 */
class TokenIterator implements Iterator
{
   
/**
     * The string to tokenize.
     * @var string
     */
   
protected $_string;
   
   
/**
     * The token delimiters.
     * @var string
     */
   
protected $_delims;
   
   
/**
     * Stores the current token.
     * @var mixed
     */
   
protected $_token;
   
   
/**
     * Internal token counter.
     * @var int
     */
   
protected $_counter 0;
   
   
/**
     * Constructor.
     * 
     * @param string $string The string to tokenize.
     * @param string $delims The token delimiters.
     */
   
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;
    }
}
?>
2011-05-13 15:10:54
http://php5.kiev.ua/manual/ru/function.strtok.html
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.
?>
2012-07-15 21:47:47
http://php5.kiev.ua/manual/ru/function.strtok.html
<?php
// strtok example
$str 'Hello to all of Ukraine';
echo 
strtok($str' ').' '.strtok(' ').' '.strtok(' ');
?>
Result:
Hello to all
2013-09-18 19:11:59
http://php5.kiev.ua/manual/ru/function.strtok.html
Автор:
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>";
}
?>
2014-02-11 19:12:24
http://php5.kiev.ua/manual/ru/function.strtok.html
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.
2014-05-30 11:15:26
http://php5.kiev.ua/manual/ru/function.strtok.html
Remove GET variables from the URL
<?php
$url 
strtok('ref.strings?foo=1&bar=2''?');
// $url = 'ref.strings'
2014-10-07 13:01:04
http://php5.kiev.ua/manual/ru/function.strtok.html
Remove GET variables from the URL
<?php
echo strtok('http://example.com/index.php?foo=1&bar=2''?');
?>
Result:
http://example.com/index.php
2015-01-06 10:40:10
http://php5.kiev.ua/manual/ru/function.strtok.html
@maisuma you invert paramaters of explode() and strtok() functions, your code does not do what you expect.
You expect to read the input string token after token so equivalent code for strtok() is  arra_filter(explode()) because explode() return lines of empty string when several delimiters are contiguous in the read string, for example  2 whitespaces between.

In fact strtok() is much faster (x2 at least) than arra_filter(explode()) if the read string contains several contiguous delimiters , 
 it is slower if the read string contains one and only one delimiter between tokens.

<?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;

?>
2016-11-15 15:41:21
http://php5.kiev.ua/manual/ru/function.strtok.html
I found this useful for parsing user entered links in text fields.

e.g. This is a link <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>
    }
2019-07-24 21:04:01
http://php5.kiev.ua/manual/ru/function.strtok.html
Автор:
After obtaining zero or more tokens with calls to strtok, you can obtain the remainder of the input string by calling strtok with an empty string as the delimiter.
2021-02-07 16:58:18
http://php5.kiev.ua/manual/ru/function.strtok.html
Hello, portuguese documentation of strtok is wrong, at this part which the example(2) is wrong.

Exemplo #2 Comportamento antigo da strtok()
<?php
$first_token 
strtok('/something''/');
$second_token strtok('/');
var_dump ($first_token$second_token);
?>

O exemplo acima produzirá:

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

(this example above, should be inverted as this:)

Correct: 
    string(9) "something"
    string(0) ""

(exemple 3 is correct)
Exemplo #3 Novo comportamento da strtok()
<?php
$first_token 
strtok('/something''/');
$second_token strtok('/');
var_dump ($first_token$second_token);
?>

O exemplo acima produzirá:

    string(9) "something"
    bool(false)
2023-04-17 23:32:51
http://php5.kiev.ua/manual/ru/function.strtok.html
Автор:
Please note that strtok memory is shared between all PHP code currently executed, even included files. This can bite you in unexpected ways if you are not careful.

For example:

<?php

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

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

$file_name strtok('');

?>

Seems easy enough, but if your Module class is not loaded, this triggers the autoloader. The autoloader *MAY* use strtok inside its loading code.

Or your Module class *MAY* use strtok inside its constructor.

This means you will never get your $file_name correctly.

So: you should *always* group strtok calls, without any external code between two strtok calls.

This would be OK:

<?php

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

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

?>

This might cause issues:

<?php

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

?>

Because your autoloader might be using strtok.

This would be avoided by fetching all parameters used in strtok *before* the calls:

<?php

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

?>
2023-11-26 18:00:48
http://php5.kiev.ua/manual/ru/function.strtok.html

    Поддержать сайт на родительском проекте КГБ