拥有单引号字符串:
$content = '\tThis variable is not set by me.\nCannot do anything about it.\n';
我想inerpret /处理字符串,好像它是双引号。换句话说,我想将实际值替换为所有可能的 escape characters (不仅仅是此示例中的制表符和换行符),并考虑到反斜杠也可能被转义,因此'\\ n'需要被'\ n'替换。 eval()可以轻松完成我的需要,但我无法使用它。
有一些简单的解决方案吗?
(一个 similar thread ,我发现在替换转义字符时,处理单引号字符串中变量的扩展。)
答案 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 well,cstripslashes
和preg_replace
提供简单而甜蜜的内容。)
如果stripcslashes
不合适,则有DoubleQuoted
。它的构造函数接受一个字符串,它被视为双引号字符串(减去变量替换,只有字符转义序列)。
如手册所示,有多个转义序列。它们看起来像正则表达式,并且都以\
开头,因此它实际上使用正则表达式来替换它们。
但是有一个例外:\\
将跳过转义序列。正则表达式需要有回溯和/或原子组来处理它,我不熟悉那些,所以我只是做了一个简单的技巧:我只将正则表达式应用于不包含{的字符串的那些部分{1}}首先简单地爆炸字符串,然后重新插入它。
基于正则表达式的两个替换函数preg_replace
Doc和preg_replace_callback
Doc也允许对数组进行操作,因此这很容易做到。
在__toString()
Doc函数中完成:
\\
查看explode
Doc和implode
Doc来电。那些注意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_replace
Doc和stripcslashes
,有一种非常简单的方法可以做到这一点,两者都是内置的:
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
以获得免费提升