首页 > 技术文章 > PHP中,4种方式定义字符串时的细节(单引号,双引号,heredoc,nowdoc)

html55 2020-05-18 16:56 原文

先上总结:

字符串用单引号来定义时,只有两个字符需要转义,一个是单引号,一个是反斜线。
字符串用双引号来定义时,双引号想表示字面值需要转义,反斜线想表示字面值需要转义,$想表示字面值需要转义,\r\n表示换行(windows下),\t表示tab,$xx表示变量xx,还有\v等不常用的略
换行符就是一个长相怪异的普通字符,说它长相怪异,是因为字母a就是占一个字母的位置,字母空格就是一个空格,而换行符占了一个行尾,且导致它后面的内容要到下一行显示,不要看成多特殊。唯一的特殊之处在于在双引号定义的时候,有个替身可用\r\n,同样的字符有tab用\t这个替身演员,不用替身演员的话,直接键盘打出也是可以的。

 

php中对字符串的处理(下面内容是在php5.4.45下测试的)

php中字符串是 用单引号或者双引号来括起来的方式。

 

先说说用单引号定义字符串的情况。

    $str = '这是一个字符串';

这是个很普通的符合人类的直觉的字符串,但是如果在字符串内容含有单引号呢?

如下这样用可以吗?

    $str = '这是一个'字符串';

有问题了:不加以处理,系统怎么知道中间的单引号是内容,还是边界符呢?

PHP中是用反斜线(\)转义,就是 \' 表示 '

    $str = '这是一个\'字符串';

这又产生了一个新问题,用了反斜线(\)转义,那内容中有反斜线怎么表示呢?

系统怎么知道反斜线是反斜线本身,还是具有转义功能的转义符呢?

所以,想在字符串中表示反斜线,那么,还需要一个反斜线转义,就是\\表示\

    $str1 = '这是一个\\\'字符串';
    echo $str1;

显示结果如下图:

在用单引号定义字符串的情况下,只有两个字符需要转义!英文状态的单引号'和英文状态反斜线(\)【注:反斜线在中文输入法状态下是顿号】

在我测试时发现,如果不是为了转义单引号,反斜线是不会被认为是转义符的,而是被认为是内容(但是最好别这样用,就用\\表示\最好)。如下:

    $str1 = '这是一个\字符串';
    echo $str1;

执行结果:

这里又有问题了,反斜线如果不和单引号在一起,就不作为转义符出现的话,反斜线和反斜线在一起,是不是转义符呢?

答:反斜线和反斜线在一起,前面的是转义符,后面的是内容,也就是说,一个反斜线是反斜线,二个反斜线是一个反斜线,3个反斜线是2个反斜线,4个反斜线是2个反斜线,5个反斜线是3个反斜线。以此类推。

如下;

    $str1 = '这是一个\"字符串';
    $str2 = '这是一个\\"字符串';
    var_dump($str1 === $str2); //bool(true),因为$str1中反斜线后面没有要转义的字符,所以\和\\一样)

总结:字符串用单引号来定义时,单引号内,只有两个字符需要转义,一个是单引号,一个是反斜线。其中反斜线又特殊了,

反斜线后面有反斜线或单引号时,它就表示转义符,反斜线后面无反斜线或单引号时,它表示它的字面值,是字符串内容的一部分。

为何反斜线不和单引号一样,需要严格的转义才能用?搞不清楚。

 

 

 1     $str1 = '这是一个\字符串';
 2     echo $str1; //这是一个\字符串
 3 
 4     $str1 = '这是一个\\字符串';
 5     echo $str1; //这是一个\字符串
 6 
 7     $str1 = '这是一个\\\字符串';
 8     echo $str1; //这是一个\\字符串
 9 
10     $str1 = '这是一个\\\\字符串';
11     echo $str1; //这是一个\\字符串

反正就是从左往右看,\\和\'表示\和',其他情况下,\就是\

    $str1 = '这是一个\\'字符串';
    echo $str1; //报错,\\表示了内容\,单引号缺转义符

我们知道,在php中#、//、/* */都是注释,那么下面的情况

    $str1 = '这是一个//字符串';
    $str2 = '这是一个#字符串';

会把绿色阴影部分当成注释,而报错吗?

答:实验可知,php单引号括起来的字符串中,只有反斜线和单引号需要转义,其他的一切视为字符,//和#也就是普通字符,不会被视为注释了代码,不会报错。

 结果如图

到目前为止,字符串都是一行搞定的,那如果字符串中有换行,空白,等视觉上看不到的字符呢?怎么定义?

试着把\n写到字符串中。

1     $str1 = '这是一个\n字符串';
2     echo $str1;    // \的后面不是\或',\将被视同内容,而非转义符。因此,这里\n是内容\和n,而不是换行的意思。
3     //这是一个\n字符串

下面这段代码和上面一样,虽然多了个\

1     $str1 = '这是一个\\n字符串';
2     echo $str1;    // \的后面是\或',视同内容\或',因此,这里\\n是字符\n,显示结果如下
3     //这是一个\n字符串

下面的代码,浏览器得到的是(查看源代码中看)

    $str1 = '这是一个<br />字符串';
    echo $str1;

  

\n被认为是字符\n,那如果想让字符串中有控制字符\n,就是换行,而不是字符\n,怎么办?

答案是:可以跨行写,如下

可见:php中,控制字符和常接触的字符一样,只不过不像ABCD那样直观,但是是存在的,直接在变量赋值等情况时打印即可。

导致的结果是在php代码中看,代码跨度好几行。

 并且在双引号中来定义。如下是等效的。

 

 

 

下面说字符串用双引号定义的情况。

    $str1 = "这是'一个";
    echo $str1; //这是'一个

 

 双引号中的单引号是不需要转义的,但是出现双引号必须转义

    $str1 = "这是"一个";
    echo $str1; //报错

 

    echo "这是\'个";   //这是\'个
    echo "这是\\'个";   //这是\'个

 字符串用双引号定义表示时,字符串内容本身若有双引号,则需要转义双引号。

同时,反斜线后面跟某些特定字符时,它是作为转义字符,如下。

\r\n表示换行(windows平台下),\t表示tab,\$表示$(要不然会被认为是变量),\v垂直制表 等。

反斜线后面跟的不是特定字符时,和单引号时一样,反斜线作为反斜线内容本身。

 

1 $str1 = "我\n\r是";
2 $str2 = "3 是";
4 var_dump($str1 === $str2); //true, 注意:编辑器和浏览器都是windows平台下的, windows平台下\r\n是换行符,unix下\n是换行符,mac下\r是换行符
5 echo $str1;

 这个字符串就是 : 我[换行]是

 显示结果如下图

 

 

总结一下,单引号和双引号来表示字符串时候的区别。

把自己当成解析器,

看到两个单引号中间夹着内容的时候,从左往右开始解析,\'表示' ,\\表示\,\其他的时候\还是\

看到双引号夹着内容的时候,\"表示",\\表示\,\$表示$,\r\n表示换行(windows下),\t表示tab,$xx表示变量xx,还有\v等不常用的略

单引号因为需要转义的少,性能高。

 

在双引号中,\r\n是换行的意思,如果在代码中直接回车也行,不一定要用\r\n,直接写的话,代码就是下面这样子

如果是用单引号的话,想写换行,就只能用上图中$str2中的方式,不能用\r\n替代换行,因为单引号下它不转义!

在php源代码文件中,换行是\r\n还是\n,还是\r,是可以设置的,如下图

 

如果字符串内容很多,还需要换行,heredoc和nowdoc的方式更方便点,因为不用考虑字符串中有单引号,双引号还要转义的事情了。

heredoc和双引号相比,也就是不用对双引号特殊处理这点优势,其他的优势不明显。

你能换行写,我也能换行写。你会对变量解析,我也一样,你支持控制字符\r\n等,我也一样。但是在html中,常用引号,可以不对引号特殊处理,也算是个实用的有点了。

nowdoc是php5.3新出的,网上可找,类似于单引号的方式,不再多说。

到这里,我觉得是纠正了以前的模糊不清的概念——控制字符也是字符,把控制字符当成普通字符看,在用双引号定义字符串时,控制字符多个物化的替身。

比如,在编辑php源代码时候,写下如下代码在文件中。

1 $str = "我是字符串
2 呀!";
3 echo $str;

 

 上面在文本编辑器中,是三行代码,不清楚这一块的可能会把变量$str理解成“我是字符串呀!”因为有个回车是看不见摸不到的,其实,把回车看出普通字符更好理解。变量$str是“我是字符串[回车]呀!”,windows下\r\n也是控制字符回车的意思因此下面的代码和上面的一样!

1 $str = "我是字符串\r\n呀!";
2 echo $str;

 

 变量$str是“我是字符串[回车]呀!”(不含引号),那么在双引号可以解析控制字符的情况下,\r\n和你单独打一个回车,在文本编辑器中显示不同,一个是看得见的\r\n,一个是看不见但起了变化的控制符,效果却是相同的

变量的内容,就是php输出的内容,也就是浏览器接到的内容,也就是浏览器上查看html源代码时的内容,至于在显示层面上怎么变化,那是另外的话题了。

 

 

另外,看看用户表单输入获得的变量和字符串之间的关系。

我在表单中input输入了 aaa'bbb ,如下图,那么提交后,通过$c = $_POST得到的是什么情况呢?

如下图:得到的变量和下面的变量是一样一样的。

1     $a = 'aaa\'bbb';
2     $b = "aaa'bbb";

这时候我就疑惑了,没有开启魔术引号的情况下。如上面代码的行1,怎么已经将单引号转义了?这样的情况还会有SQL注入问题吗?为什么会自动加反斜线?

其实,这个逻辑是这样的,为了便于理解,我就称呼  aaa'bbb  为  矮矮矮单引号比比比,共有7个字符

用户在input里输入:矮矮矮单引号比比比

php页面post收到字符串内容:矮矮矮单引号比比比

我们用单引号定义变量时显示:矮矮矮反斜线单引号比比比,这里就是疑惑所在了。

这样理解,之所以有反斜线显示出来,就是把脑海中的字符串内容显示成带有单引号包装的代码,碰到单引号,所以加反斜杠转义。

它的实质是,这个变量是一个字符串,这个字符串内容就是,矮矮矮单引号比比比。

所以,用双引号定义变量时,没有反斜线,它们也是完全相等的,就是,矮矮矮单引号比比比

$a = 'aaa\'bbb';解释为变量a就是内容为矮矮矮单引号比比比这7个字符的字符串。

echo 这个变量,也是矮矮矮单引号比比比

在组成传统的sql语句过程中,变量带有单引号,sql语句就变成了一个内容为带有不正确数目的单引号的新字符串。

通过addslashes函数后,变量加了反斜线,变成了矮矮矮反斜线单引号比比比,这时候再组成的sql语句,数据库执行的时候,读反斜线单引号为单引号,存到数据库中,于是恢复了原貌。

有点绕,所以按照新的sql方式,数据和定界符分开,也就没这么多破事了。

推荐阅读