字符串函数
在线手册:中文 英文
PHP手册

sscanf

(PHP 4 >= 4.0.1, PHP 5)

sscanfParses input from a string according to a format

说明

mixed sscanf ( string $str , string $format [, mixed &$... ] )

The function sscanf() is the input analog of printf(). sscanf() reads from the string str and interprets it according to the specified format, which is described in the documentation for sprintf().

Any whitespace in the format string matches any whitespace in the input string. This means that even a tab \t in the format string can match a single space character in the input string.

参数

str

The input string being parsed.

format

The interpreted format for str, which is described in the documentation for sprintf() with following differences:

  • Function is not locale-aware.
  • F, g, G and b are not supported.
  • D stands for decimal number.
  • i stands for integer with base detection.
  • n stands for number of characters processed so far.

...

Optionally pass in variables by reference that will contain the parsed values.

返回值

If only two parameters were passed to this function, the values parsed will be returned as an array. Otherwise, if optional parameters are passed, the function will return the number of assigned values. The optional parameters must be passed by reference.

If there are more substrings expected in the format than there are available within str, -1 will be returned.

范例

Example #1 sscanf() Example

<?php
// getting the serial number
list($serial) = sscanf("SN/2350001""SN/%d");
// and the date of manufacturing
$mandate "January 01 2000";
list(
$month$day$year) = sscanf($mandate"%s %d %d");
echo 
"Item $serial was manufactured on: $year-" substr($month03) . "-$day\n";
?>

If optional parameters are passed, the function will return the number of assigned values.

Example #2 sscanf() - using optional parameters

<?php
// get author info and generate DocBook entry
$auth "24\tLewis Carroll";
$n sscanf($auth"%d\t%s %s"$id$first$last);
echo 
"<author id='$id'>
    <firstname>
$first</firstname>
    <surname>
$last</surname>
</author>\n"
;
?>

参见


字符串函数
在线手册:中文 英文
PHP手册
PHP手册 - N: Parses input from a string according to a format

用户评论:

nmmm at nmmm dot nu (23-Nov-2011 01:11)

This is more like C/C++ example, but works on PHP too.

<?php
$qs
= "index.php?id=34&name=john";

print_r(   sscanf($qs, "%[^?]?%[^?]")   );

$qs = "id=34&name=john";

print_r(   sscanf($qs, "id=%[^&]&name=%[^&]")   );
?>

Igor Feghali (22-Oct-2008 06:56)

parses an input string with fixed field sizes that contains data with spaces:

<?php
$result
= sscanf("  Vendor: My Vendo Model: Super Model Foo  Rev: 1234"
                
'  Vendor: %8[ -~] Model: %16[ -~] Rev: %4c',
                
$vendor, $model, $rev);
?>

$vendor => My Vendo
$model => Super Model Foo
$rev => 1234

leg (13-Aug-2008 05:20)

@mikewillitsgmail.com:

<?php

$out
= sscanf('file_name.gif', 'file_%[^.].%s', $fpart1, $fpart2);

echo
'<pre>';
print_r($fpart1);
echo
'<hr />';
print_r($fpart2);
echo
'</pre>';

?>

output:

name
-
gif

The "^." part avoid the first searched string to be too greedy. But doesn't protect you against an "file_test.name.gif" input, with bad results!

--==[FReeZ]==-- (27-May-2008 09:59)

sscanf() in PHP also supports the more advanced patterns like in C++.
That means you can use it a lot more, after readining the details from http://www.cplusplus.com/reference/clibrary/cstdio/sscanf.html

Example usage:
$date = 'january-2008';
sscanf($date, '%[a-z]-%d', $month, $year);
printf('Parsed values: "%s", "%d"', $month, $year);

// Outputs Parsed values: "january", "2008"

mikewillitsgmail.com (15-Mar-2008 07:13)

FYI - if you are trying to scan from a string which contains a filename with extension. For instance:

<?php

$out
= sscanf('file_name.gif', 'file_%s.%s', $fpart1, $fpart2);

?>

The scanned string in the $fpart1 parameter turns out to be 'name.gif' and $fpart2 will be NULL.

To get around this you can simply replace the "." with a space or another "white-space like" string sequence.

I didn't see any other comments on regarding string literals which contain a '.' so I thought I'd mention it. The subtle characteristics of having "white-space delimited" content I think can be a source of usage contention. Obviously, another way to go is regular expressions in this instance, but for newer users this may be helpful.

Just in case someone else spent 10 minutes of frustration like I did. This was seen on PHP Version 5.2.3-1ubuntu6.3.

Searching the bug reports shows another users misunderstanding: http://bugs.php.net/bug.php?id=7793

Hayley Watson (14-Aug-2007 04:38)

Just to note something not explicitly mentioned in the documentation.

sscanf() matches as much of the passed string as it can against the format. If it gets part way through and discovers that the next part of the string fails to match the format it gives up; it will return the parts that it did match, and NULL for the remainder.

Rodrigo Araújo (12-Oct-2006 09:02)

Important warning about the last note by "anonymouse" (quoting):
----------------------------------------------------------------------
sscanf($string_to_parse,'%d %[^$]s',$num,$text);

This conversion command says "look for an integer, then a space, then any string up to the end of the string"
----------------------------------------------------------------------

This won't do what you think. You have the ^ and $ characters inside square brackets: $text will be empty if $string_to_parse contains for example "123 $".

What that pattern really means is: match an signed integer, followed by a whitespace, followed by a string *NOT* containing a dollar sign, followed by an 's'.

To do what you want i'm afraid you really must use ereg. Scanf is *not* a regular expression parser. It was designed to be used in plain C, where you don't have arbitrary length strings natively. Also, in scanf patterns you don't have an equivalent to matching the beginning/end of string, like you have with ^ and $ in regular expressions.

In PHP, you should use something like:
if (ereg('^([+-]?[0-9]+) (.+)$', $string_to_parse, $regs)) {
  $num = $regs[1];
  $text = $regs[2];
}
This regular expression assumes you want a signed integer at the beginning, and you don't mind if it starts with 0 when it is non-zero, e.g. 0123, +0321 or -0456 is also valid.

anonymouse (02-Aug-2006 05:38)

I've seen several examples of people using brackets to define what look like regexp character classes. In my limited testing I don't think they are genuine character classes but they seem to be similar.

My task was to use sscanf() to parse an array of strings with the format:

number SPACE string_which_may_also_have_spaces

The normal %s conversion command treats spaces as some kind of delimiter. So, you can get the strings if you know beforehand how many "words" there will be. But, my input was variable.

Here's what I came up with: (note use of the dollar-sign 'end of string' hidden delimiter)

sscanf($string_to_parse,'%d %[^$]s',$num,$text);

This conversion command says "look for an integer, then a space, then any string up to the end of the string"

skeltoac (23-Mar-2006 04:28)

To parse a line from an Apache access log in common format:

<?php
$log
= array();
$n = sscanf(trim($line), '%s %s %s [%[^]]] "%s %s %[^"]" %d %s "%[^"]" "%[^"]"',
   
$log['ip'],
   
$log['client'],
   
$log['user'],
   
$log['time'],
   
$log['method'],
   
$log['uri'],
   
$log['prot'],
   
$log['code'],
   
$log['bytes'],
   
$log['ref'],
   
$log['agent']
);
?>

Fuzzmaster (23-Sep-2005 03:14)

Building a better Phone Format.  This function, a variation of prev. posts (in theory):

Should stip all non-numeric characters.
Treat partial numbers as extensions.
Treat 8 - 9 digit numbers as phone+ext.

Be kind I'm a newbie.

<?php

   
// ##### Format String as Phone Number
   
Function formatPH($ph)
          {
          
$ph = ereg_replace ('[^0-9]+', '', $ph); // ##### Strip all Non-Numeric Characters
          
$phlen = strlen($ph);
           switch (
TRUE)
             {
              case (
$phlen < 7):
               
$ext = $ph;
                break;
              case (
$phlen == 7):
               
sscanf($ph, "%3s%4s", $pfx, $exc);
                break;
              case (
$phlen > 7 AND $phlen < 10):
               
sscanf($ph, "%3s%4s%s", $pfx, $exc, $ext);
                break;
              case (
$phlen == 10):
               
sscanf($ph, "%3s%3s%4s", $area, $pfx, $exc);
                break;
              case (
$phlen == 11):
               
sscanf($ph, "%1s%3s%3s%4s", $cty, $area, $pfx, $exc);
                break;
              case (
$phlen > 11):
               
sscanf($ph, "%1s%3s%3s%4s%s", $cty, $area, $pfx, $exc, $ext);
                break;
             }
          
$out = '';
          
$out .= isset($cty) ? $cty.' ' : '';
          
$out .= isset($area) ? '('.$area.') ' : '';
          
$out .= isset($pfx) ? $pfx.' - ' : '';
          
$out .= isset($exc) ? $exc.' ' : '';
          
$out .= isset($ext) ? 'x'.$ext : '';
           return
$out;
          }

?>

Brainiac361 (22-Aug-2005 09:49)

The %[^[]]-trick may seem to work, but it doesn't!

What happens is that sscanf will simply match any characters but an opening square bracket (which is rather rare and that's why it might just seem to work).
But even worse it will expect a ]-character next and continue to match anything.

Now what you can do is make sscanf look for any character but a character that is really never used... a good choice is the linebreak "%[^\\n]", especially in combination with fscanf.

What you can also do is copy and paste any unused ascii character like #001 or something.

joshmckenneyATgmailDOT(0{ (14-Jul-2005 11:21)

added country code (1) to phone number function:

function formatPhone($phone) {
       if (empty($phone)) return "";
       if (strlen($phone) == 7)
               sscanf($phone, "%3s%4s", $prefix, $exchange);
       else if (strlen($phone) == 10)
               sscanf($phone, "%3s%3s%4s", $area, $prefix, $exchange);
       else if (strlen($phone) > 10)
               if(substr($phone,0,1)=='1') {
                                 sscanf($phone, "%1s%3s%3s%4s", $country, $area, $prefix, $exchange);
                             }
                             else{
                                 sscanf($phone, "%3s%3s%4s%s", $area, $prefix, $exchange, $extension);
                                }
       else
               return "unknown phone format: $phone";
       $out = "";
       $out .= isset($country) ? $country.' ' : '';
       $out .= isset($area) ? '(' . $area . ') ' : '';
       $out .= $prefix . '-' . $exchange;
       $out .= isset($extension) ? ' x' . $extension : '';
       return $out;
}

Vincent Jansen (16-Jun-2005 09:04)

If you just wants filter out information between two parts of a string, i used the following, it works better for me then the sscanf function.

<?php
function scanstr($zoekstr,$part1,$part2) {
$firstpos=strpos ($zoekstr, $part1)+strlen($part1);
$lastpos=strpos ($zoekstr, $part2);
$scanresult=substr ($zoekstr, $firstpos, $lastpos-$firstpos);
    return(
$scanresult);
}
echo
scanstr ("var1=hello&var2=test&var3=more","var2=","&var3");
?>

codeslinger at compsalot dot com (06-Feb-2005 02:03)

Security Note:

Although it is a very powerful technique, keep in mind that it is easily deceived.

Many successful exploits have been based on scanf attacks.  It should not be used on untrusted input without a lot of additional validation.

steve (atsign) rapaport *fullstop* com (19-Sep-2004 12:27)

The classic example
<?php
list($month, $day, $year) = sscanf($mandate, "%s %d %d");
?>

works fine if you already know the variable names you want to fill, and the format string you want to fill them with.  If you want both of those to be dynamically configurable (as in reading a file with a flexible format), you may have a use for this code:

Assumes your variables are listed in the variable
$varstr = "var1, var2, var3" 
(you don't know what the variable names will be,
 perhaps they're a subset of a long list of valid ones)

Assumes your input line is in $line, and your sscanf spec is in $spec.  Both of these may also be in your input.

Integrity checks omitted for clarity.

<?php
        $vars
= explode(',', $varstr);

       
$values = @sscanf($line, $spec);
       
// Do input integrity checks

       
foreach ($vars as $var) {
           
$var = trim($var);
           
$next = each($values);
            $
$var = $next['value'];
            if ($
$var) $result_array[$var] = $$var;
        }
?>

This took me some time to write successfully, so I'm including it for posterity.  Non-obvious is the use of
$next.

Cheers,

Steve

marcus at synchromedia dot co dot uk (24-Mar-2003 05:38)

In PHP >= 4.3.0, if you use additional reference parameters, you will get this warning:

PHP Warning:  Call-time pass-by-reference has been deprecated - argument passed by value

This clearly has the potential to cause unexpected consequences (vars left empty), and will break existing code. So don't do it! These docs need updating to say this too.

The syntax:

    list($a, $b) = sscanf("hello world", "%s %s");

will work as expected, and doesn't seem to cause any problems with Apache that I've noticed.

sbarnum.pointsystems@com (19-Feb-2003 09:29)

This is a better phone format function than the one above, handles leading zeroes correctly, by using the "%s" instead of "%d" argument to sscanf().  Again, this function assumes that the input has been stripped of all non-integer values.

function formatPhone($phone) {
        if (empty($phone)) return "";
        if (strlen($phone) == 7)
                sscanf($phone, "%3s%4s", $prefix, $exchange);
        else if (strlen($phone) == 10)
                sscanf($phone, "%3s%3s%4s", $area, $prefix, $exchange);
        else if (strlen($phone) > 10)
                sscanf($phone, "%3s%3s%4s%s", $area, $prefix, $exchange, $extension);
        else
                return "unknown phone format: $phone";
        $out = "";
        $out .= isset($area) ? '(' . $area . ') ' : "";
        $out .= $prefix . '-' . $exchange;
        $out .= isset($extension) ? ' x' . $extension : "";
        return $out;
}

sbarnum.pointsystems@com (20-Nov-2002 03:21)

More fun with phones!  This assumes that the phone number is 10 digits, with only numeric data, but it would be easy to check the length of the string first.

function formatPhone($phone) {
        if (empty($phone)) return "";
        sscanf($phone, "%3d%3d%4d", $area, $prefix, $exchange);
        $out = @$area ? "($area) " : "";
        $out .= $prefix . '-' . $exchange;
        return $out;
}

jon at fuck dot org (13-Sep-2002 07:27)

this function is a great way to get integer rgb values from the html equivalent hex.

list($r, $g, $b) = sscanf('00ccff', '%2x%2x%2x');

jamesDOTwatsonATrealismstudiosDOTcom (10-Jul-2002 08:31)

You're making a common mistake in believing that sscanf (and the whole scanf family) are more intelligent than they really are, sscanf always starts checking from the first character of the string, the first character that doesn't match the format string causes it to bail out early, if you want to match later in the string you'll have to do something like this:

sscanf(strstr($lit[$x],"Vol."),"Vol. %d",&$vol[$x]);

(check the docs on strstr if you're unsure of how it works)

or if you want you can add some simple error correction

$temp=strstr($lit[$x],"Vol.");
if ($temp)
    sscanf($temp,"Vol. %d",&$vol[$x]);
else
    $vol[$x]=$default_vol;

elgabos at umail dot ucsb dot edu (27-Feb-2002 11:38)

After playing around with this for a while, I found that if you use %[^[]] instead of %s (since php has problems with spaces when using %s) it works nicely.

For those that aren't familiar with regular expressions, %[^[]] basically matches anything that isn't nothing.

Hope this helps. - Gabe

narainsbrain at yahoo dot com (14-Oct-2001 11:25)

apparently, sscanf always splits at spaces, even if spaces are not specified in the format. consider this script:

<?php
$str
= "This is a\tsentence with\ttabs";
$scanned = sscanf($str, "%s\t%s\t%s");
echo
join(" : ", $scanned);
?>

this echoes "This : is : a", not the expected "This is a : sentence with : tabs."
this behaviour is fine if your strings don't contain spaces, but if they do you'd be better off using explode().

owenh at halo5 dot com (28-Nov-2000 12:21)

<?php list($phone11, $phone12, $phone13) = sscanf($arow[9],"(%d) %d-%d"); ?>

If thoes variables dont have data in them it seems to cause segfaults on apache.  ( just to save you some trouble shooting time here is my fix:
<?php sscanf($arow[9],"(%d) %d-%d", &$phone11,&$phone12, &$phone13); ?>

clcollie at mindspring dot com (12-Sep-2000 12:59)

Actually sscanf()_always_ returns an array if you specify less return variables than format specifiers. i may change this to return a scalar if only a single format specifier exists.
  Note that sscanf() is (almost) the complete functional equivalent of its "C" counterpart, so you can do the following to get the expected effect :

   sscanf("SN/2350001","SN/%d",&$serial)

The array return was a nicety for PHP.