解释单引号字符串中的转义字符

时间:2011-11-29 11:09:47

标签: php string escaping

拥有单引号字符串:

$content = '\tThis variable is not set by me.\nCannot do anything about it.\n';

我想inerpret /处理字符串,好像它是双引号。换句话说,我想将实际值替换为所有可能的 escape characters (不仅仅是此示例中的制表符和换行符),并考虑到反斜杠也可能被转义,因此'\\ n'需要被'\ n'替换。 eval()可以轻松完成我的需要,但我无法使用它。

有一些简单的解决方案吗?

(一个 similar thread ,我发现在替换转义字符时,处理单引号字符串中变量的扩展。)

3 个答案:

答案 0 :(得分:5)

如果您需要像PHP那样执行完全转义序列,则需要长版本,即DoubleQuoted类。我将输入字符串扩展了一些,以覆盖比你的问题更多的转义序列,以使其更通用:

$content = '\\\\t\tThis variable\\string is\x20not\40set by me.\nCannot \do anything about it.\n';

$dq = new DoubleQuoted($content);

echo $dq;

输出:

\\t This variable\string is not set by me.
Cannot \do anything about it.

但是,如果您可以接近,那么有一个名为stripcslashes的PHP函数,为了进行比较,我添加了它的结果和PHP双引号字符串:

echo stripcslashes($content), "\n";

$compare = "\\\\t\tThis variable\\string is\x20not\40set by me.\nCannot \do anything about it.\n";

echo $compare, "\n";

输出:

\t  This variablestring is not set by me.
Cannot do anything about it.

\\t This variable\string is not set by me.
Cannot \do anything about it.

正如您所看到的,stripcslashes删除了一些与PHP本机输出相比的字符。

修改: See my other answer as wellcstripslashespreg_replace提供简单而甜蜜的内容。)

如果stripcslashes不合适,则有DoubleQuoted。它的构造函数接受一个字符串,它被视为双引号字符串(减去变量替换,只有字符转义序列)。

如手册所示,有多个转义序列。它们看起来像正则表达式,并且都以\开头,因此它实际上使用正则表达式来替换它们。

但是有一个例外:\\将跳过转义序列。正则表达式需要有回溯和/或原子组来处理它,我不熟悉那些,所以我只是做了一个简单的技巧:我只将正则表达式应用于不包含{的字符串的那些部分{1}}首先简单地爆炸字符串,然后重新插入它。

基于正则表达式的两个替换函数preg_replaceDocpreg_replace_callbackDoc也允许对数组进行操作,因此这很容易做到。

__toString()Doc函数中完成:

\\

查看explodeDocimplodeDoc来电。那些注意class DoubleQuoted { ... private $string; public function __construct($string) { $this->string = $string; } ... public function __toString() { $this->exception = NULL; $patterns = $this->getPatterns(); $callback = $this->getCallback(); $parts = explode('\\\\', $this->string); try { $parts = preg_replace_callback($patterns, $callback, $parts); } catch(Exception $e) { $this->exception = $e; return FALSE; # provoke exception } return implode('\\\\', $parts); } ... 不对包含preg_replace_callback的任何字符串进行操作。因此,替换操作已经摆脱了处理这些特殊情况的负担。这是\\为每个模式匹配调用的回调函数。我将它包装成一个封闭装置,因此无法公开访问:

preg_replace_callback

您需要一些其他信息才能理解它,因为这不是完整的课程。我浏览了缺失的点并添加了缺失的代码:

“查找”类的所有模式都包含子组,至少包含一个子组。那个进入private function getCallback() { $map = $this->map; return function($matches) use ($map) { list($full, $type, $number) = $matches += array('', NULL, NULL); if (NULL === $type) throw new UnexpectedValueException(sprintf('Match was %s', $full)) ; if (NULL === $number) return isset($map[$type]) ? $map[$type] : '\\'.$type ; switch($type) { case 'x': return chr(hexdec($number)); case '': return chr(octdec($number)); default: throw new UnexpectedValueException(sprintf('Match was %s', $full)); } }; } 并且是要翻译的单个字符是八进制的空字符串和十六进制数字的$type

可选的第二组x未设置($number)或包含八进制/十六进制数。 NULL输入规范化为此行中刚才命名的变量:

$matches

模式被预先定义为私有成员变量中的序列:

list($full, $type, $number) = $matches += array('', NULL, NULL);

private $sequences = array( '(n|r|t|v|f|\\$|")', # single escape characters '()([0-7]{1,3})', # octal '(x)([0-9A-Fa-f]{1,2})', # hex ); 函数只是将这些定义包装到有效的PCRE正则表达式中,如:

getPatterns()

这很简单:

/\\(n|r|t|v|f|\$|")/ # single escape characters
/\\()([0-7]{1,3})/ # octal
/\\(x)([0-9A-Fa-f]{1,2})/ # hex

现在,随着模式的概述,这解释了调用回调函数时private function getPatterns() { foreach($this->sequences as $sequence) $patterns[] = sprintf('/\\\\%s/', $sequence) ; return $patterns; } 包含的内容。

要了解回调的工作方式,您需要了解的另一件事是$matches。这只是一个包含单个替换字符的数组:

$map

这对于全班来说已经非常重要了。还有另一个私有变量private $map = array( 'n' => "\n", 'r' => "\r", 't' => "\t", 'v' => "\v", 'f' => "\f", '$' => '$', '"' => '"', ); 用于存储是否抛出异常,因为$this->exception不能抛出异常,如果在回调函数中发生异常会导致致命错误。因此,它被捕获并存储到私有类变量中,这里再次是代码的一部分:

__toString()

如果替换时发生异常,则该函数与 ... public function __toString() { $this->exception = NULL; ... try { $parts = preg_replace_callback($patterns, $callback, $parts); } catch(Exception $e) { $this->exception = $e; return FALSE; # provoke exception } ... 一起存在,这将导致可捕获的异常。 getter函数使内部异常可用:

FALSE

由于访问原始字符串也很不错,您可以添加另一个getter来获取:

private $exception;
...
public function getException()
{
    return $this->exception;
}

这就是整个班级。希望这有用。

答案 1 :(得分:5)

基于preg_replaceDocstripcslashes,有一种非常简单的方法可以做到这一点,两者都是内置的:

preg_replace(
    '/\\\\([nrtvf\\\\$"]|[0-7]{1,3}|\x[0-9A-Fa-f]{1,2})/e',
    'stripcslashes("$0")', $content
);

只要"\\n"成为"\n"等,此功能就可以使用。 Demo

如果您正在寻找按字面意思处理这些字符串,请参阅我的previous answer

编辑:您在评论中提问:

  

我有点疑惑这个和stripcslashes()的输出之间的区别直接[?]

差异并不总是可见,但有一个:如果后面没有转义序列,stripcslashes将删除\字符。在PHP字符串中,在这种情况下不会删除斜杠。例如,"\d"d不是特殊字符,因此PHP保留斜杠:

$content = '\d';
$content; # \d
stripcslashes($content); # d
preg_replace(..., $content); # \d

这就是preg_replace在这里有用的原因,它只会在stripcslashes按预期工作的子串中应用函数:所有有效的转义序列。

答案 2 :(得分:0)

基于正则表达式的解决方案在这里可能是最容易维护的(字符串中有效转义序列的定义甚至在文档中作为正则表达式提供):

$content = '\tThis variable is not set by me.\nCannot do anything about it.\n';

$replaced = preg_replace_callback(
                '/\\\\(\\\\|n|r|t|v|f|"|[0-7]{1,3}|\x[0-9A-Fa-f]{1,2})/',
                'replacer',
                $content);

var_dump($replaced);

function replacer($match) {
    $map = array(
        '\\\\' => "\\",
        '\\n' => "\n",
        '\\r' => "\r",
        '\\t' => "\t",
        '\\v' => "\v",
        // etc for \f \$ \"
    );

    $match = $match[0]; // So that $match is a scalar, the full matched pattern

    if (!empty($map[$match])) {
        return $map[$match];
    }

    // Otherwise it's octal or hex notation
    if ($match[1] == 'x') {
        return chr(hexdec(substr($match, 2)));
    }
    else {
        return chr(octdec(substr($match, 1)));
    }
}

以上也可以(并且确实应该)进行改进:

  • 将替换程序函数打包为匿名函数
  • 可能会$map替换switch以获得免费提升