关于正则零宽断言的理解

最近项目里需要抓取别的网站的数据,于是重新回顾了一下正则,特别是零宽断言,下面是个人理解和总结。

基本概念说的很难理解,先上个例子:

<?php

preg_match_all("/(\w*)ing/", "aing bing", $mtches1);

preg_match_all("/(\w*)(?=ing)/", "aing bing", $mtches2);

print_r($matches1);
print_r($matches2);

刚开始我以为这两的结果是一样的,但其实不然,结果如下:

Array
(
    [0] => Array
        (
            [0] => aing
            [1] => bing
        )

    [1] => Array
        (
            [0] => a
            [1] => b
        )

)
Array
(
    [0] => Array
        (
            [0] => a
            [1] => 
            [2] => b
            [3] => 
        )

    [1] => Array
        (
            [0] => a
            [1] => 
            [2] => b
            [3] => 
        )

)

还是太天真。

于是从匹配过程上分析一波。

0a1b2i3n4g5 6b7i8n9g : 数字代表位置

第一个:先从位置0开始匹配,后的字符a匹配到\w,继续后面ing虽然能匹配到\w,但是它们是确定字符,所以匹配到第一个aing,于是继续从位置5开始,后面是空格,匹配不到\w,到位置6,b匹配到\w,继续向后,匹配到第二个bing,结束。于是分组1捕获的就是a和b。

第二个:同样从位置0开始,后的字符a匹配到\w,跳到位置1,此时控制权会交给后面的(?=ing),它断言位置1后面是ing字符,一看还真是,于是匹配到a,其实这里分不分组都一样,因为(?=exp)是零宽度,就是只占一个位置,继续从位置1开始往后匹配(因为(?=exp)是零宽),为什么空值成了第二个匹配到的值,我的理解是:因为*可以匹配空,也是零宽度,并且可以匹配到ing,类似于\0这种表示,也是就是说aing可能表示成a\0b\0i\0g\0这种。在往后就跟前面一样了,所以最后结果是a \0 b \0。

如果上面的\w*改成\w+,那么\0就不会被匹配到了。

看着很ok,但其实以上分析是不对的,如果把aing bing改成ainging bing,如果按照以上步骤,那么第二种情况应该是a \0 aing \0 b \0,但运行结果没有a和\0,????

其实先行断言的执行步骤是这样的先从要匹配的字符串中的最右端找到第一个ing(也就是先行断言中的表达式)然后 再匹配其前面的表达式,若无法匹配则继续查找第二个ing 再匹配第二个 ing前面的字符串,若能匹配 则匹配 ,意思是匹配是从最右边开始的,找到ing后,匹配b,找到第一个,再往前走,\0,再往前走aing,最后是\0。

负向零宽断言跟正向的两种情况是相反的。

果然正则是不适合地球人看的。

云Esc – PHP开发环境搭建

在环境搭建之前,最好先确定目录的结构,比如源码安装在/usr/local/src,应用安装在/usr/local

1. 编译安装apache

编译安装apache之前,需要先编译几个apache运行时需要依赖的组件,官网下载地址,下面依次介绍: 1 . Apr

编译步骤如下

cd /usr/local/src 
wget http://apache.forsale.plus//apr/apr-1.6.3.tar.gz
tar -zxvf apr-1.6.3.tar.gz 
cd apr-1.6.3
./configure --prefix=/usr/local/apr
make && make install

2 . Apr-Util

编译步骤如下

wget http://apache.forsale.plus//apr/apr-util-1.6.1.tar.gz
tar -zxvf apr-util-1.6.1.tar.gz 
cd apr-util-1.6.1
./configure --prefix=/usr/local/apr-util --with-apr=/usr/local/apr
make && make install

3 . Pcre

编译步骤如下

 

wget https://sourceforge.net/projects/pcre/files/pcre/8.41/pcre-8.41.tar.gz/download
tar -zxvf download
cd pcre-8.41
./configure --prefix=/usr/local/pcre
make && make install

上面几个编译安装完后,就可以安装apache了,安装步骤类似,如下:

 

wget http://apache.mirror.colo-serv.net//httpd/
tar -zxvf httpd-2.4.29.tar.gz
cd httpd-2.4.29
./configure --prefix=/usr/local/apache --with-apr=/usr/local/apr --with-apr-util=/usr/local/apr-util --with-pcre=/usr/local/pcre
make && make install

注意事项:

  1. 如果是重新编译安装,编译前执行make clean, 避免一些不必要的错误。
  2. 安装完后,修改配置文件,将 ServerName 更改成 127.0.0.1:80DocumentRoot 更改成对应的 web根目录 ,将 vhost 的配置文件的注释打开: **Include conf/extra/httpd-vhosts.conf ** 。
  3. 此时/usr/local/apache/modules的目录下,是没有libphp7.so这个扩展的,需要等php安装完后,才会自动生成。
  4. 一般需要给apache运行用户daemon分配一个额外的组,比如www用户所属组(www用户专门用于管理web,以及ftp登陆),方便日后的权限管理。

 

useradd www
usermod daemon -g www
chown -R www:www /网站根目录
chmod -R 770 /网站根目录

然后将配置文件中的 Group 更改成 www

5. 关于网站权限问题,可以有如下方案:

– 如上所说,网站由www用户管理,apache用户加入www组, 其他用户没有任何权限

– 网站由www用户管理,组和其他人不给任何权限,用acl去单独给apache用户添加权限。

– 不做任何处理,apache用户视作其他用户,给rx权限。内部需要w权限的目录单独设置。

6. 别忘了重启

httpd -k restart

2. 编译安装php7.2

编译步骤如下:

 

wget http://cn2.php.net/get/php-7.2.0.tar.gz/from/this/mirror
tar -zxvf mirrow
cd php-7.2.0
./configure ./configure --prefix=/usr/local/php7 --with-config-file-path=/usr/local/php7/lib --with-apxs2=/usr/local/apache/bin/apxs --with-mhash --with-openssl --with-mysqli --with-pdo-mysql --with-gd --with-iconv --with-zlib --enable-zip --enable-inline-optimization --enable-debug --enable-shared --enable-xml --enable-bcmath --enable-mbregex --enable-mbstring --enable-ftp --enable-pcntl --enable-sockets --enable-soap --enable-session --with-curl --enable-opcache
make && make install

注意事项:

  1. 如果是重新编译安装,编译前执行make clean
  2. 在编译安装之前,最好先保证依赖的一些扩展已经安装,可以执行:
yum -y install gcc gcc++ gcc-c++ wget make autoconf bison libxml2 libxml2-devel openssl openssl-devel curl-devel libjpeg-devel libpng-devel freetype-devel

3. 关于编译选项的含义,可以参考php手册,也可以参考 这篇文章

4. 安装完成后,需要做一下操作:

 

cp /usr/local/src/php-7.2.0/php.ini-development /usr/local/php/lib/php.ini # 复制配置文件
cp /usr/local/php7/bin/php /usr/bin/php
cp /usr/local/php7/bin/phpize /usr/bin/phpize

5. 此时libphp7.so已经编译生成,在httpd.conf里添加 AddType application/x-httpd.php .php

echo "AddType application/x-httpd-php .php" >> /usr/local/apache/conf/httpd.conf

 注:这句话既是指定php文件处理器,又是指定php文件mime-type

3. 编译安装redis,mongodb

1 . redis 首先从github上clone源码, 地址, 安装步骤如下:

 

git clone https://github.com/phpredis/phpredis.git
cd phpredis phpize
./configure --with-php-config=/usr/local/php7/bin/php-config --enable-redis-igbinary
make && make install
echo "extension=redis.so" >> `php --ini | grep "Loaded Configuration" | sed -e "s|.*:\s*||"`

2 . mongodb mongodb可以使用pecl安装:

 

pecl install mongodb
echo "extension=mongodb.so" >> `php --ini | grep "Loaded Configuration" | sed -e "s|.*:\s*||"`

注意事项

  1. 编译后的.so文件默认是放在 extension_dir 目录中,可以使用php -i | grep 'extension_dir' 查看,也可以更改php.ini中的设置。
  2. 可以使用php -m查看已经加载的module

4. 安装mysql

如果需要在服务器上安装mysql,可以参考这篇文章

5. ossftp

参考github

注意事项

如果挂载的目录,允许其他人查看,可以在挂载的时候指定 -o allow_ohter 参数。

php7.2 不向后兼容的改动

做一个简单的总结,没有涵盖全部改动,因为不常见。

1. number_format()不会返回-0

因为-0这种写法很古怪,所以number_format(-0.01),会返回0。


2. 对数组强制转成对象时,会将整形key转成string

直接看代码,

$a = [0 => 1]; $a = (object)$a; print_r($a->{0}); //或者 print_r($a->{'0'});

现在允许这么写。但是key已经变成string类型,即’0’。


3. 不可以对不能count的对象调用count函数

没有实现Countable接口的对象都不能调用count函数,比如 count(1),count(‘a’) 在之前都会返回1,但是在7.2版本以后,会返回一个E_WARNING级别的错误,看一个例子:

class _count implements Countable{
    public function count() {
        return 10;
    }
}

$count = new _count();
echo count($count);  // 10

 


4. 可以对__PHP_Incomplete_Class类型的对象调用is_object方法

__PHP_Incomplete_Class,这个东西就是有一个对象,php没有找到定义它的地方,就会把该对象的类型变成这个,举个例子:

$serialized_object='O:1:"a":1:{s:5:"value";s:3:"100";}';

print_r( unserialize($serialized_object) );

// __PHP_Incomplete_Class Object ( [__PHP_Incomplete_Class_Name] => a [value] => 100 )

在解序列化的时候很常见,可以用unserialize_callback_func选项去避免,举个例子:

$serialized_object='O:1:"a":1:{s:5:"value";s:3:"100";}';

ini_set('unserialize_callback_func', 'mycallback'); // 设置回调

print_r( unserialize($serialized_object) );
function mycallback($classname) 
{
    require_once $classname.".php";
}

而在7,2版本对__PHP_Incomplete_Class对象调用is_object(),会返回true。


5. 使用一个未定义的常量,会产生一个E_WARNING级别的错误。

echo NAME;
// Warning: Use of undefined constant NAME

6. bcmod函数对于浮点数的操作

在之前,bcmod对于浮点数的运算类似于%操作符,但是在新版类似于fmod。

echo bcmod(4,3.5);  // 之前会返回1,现在会返回1,5