oz grep源码分析

本文介绍了oz grep源码中正则表达式编译的细节,特别是针对'[1-9]'这样的字符类处理。在解析过程中,详细分析了代码流程,指出了一些关键的代码段,并通过例子解释了为何程序处理过程中不需要特地处理']'的情况。通过阅读源码,作者发现了自己对程序理解的不足,并强调了经常阅读代码对于提高编程技能的重要性。
摘要由CSDN通过智能技术生成


oz grep源码分析
今天在笔记本上折腾ubuntu18,真是很好玩。
配置低的本上,装ubuntu,真划算。原来4G跑win10总是慢得不行,现在跑linux,感觉还是很快的。
我在本子上读regex.c这个文件,一个地方卡住了。

    for (p = pat; *p; p++) {
        lp = mp;
        switch(*p) {

        case '.':               /* match any char..  */
            store(ANY);
            break;

        case '^':               /* match beginning.. */
            if (p == pat)
                store(BOL);
            else {
                store(CHR);
                store(*p);
            }
            break;

        case '$':               /* match endofline.. */
            if (!*(p+1))
                store(EOL);
            else {
                store(CHR);
                store(*p);
            }
            break;

        case '[':               /* match char class..*/
            store(CCL);

            if (*++p == '^') {
                mask = 0377;    
        //0377==11 111 111,与它异或,按位取反。:w
                p++;
            }
            else
                mask = 0;

            if (*p == '-')        /* real dash */
                chset(*p++);
            if (*p == ']')        /* real brac */
                chset(*p++);
            while (*p && *p != ']') {
                if (*p == '-' && *(p+1) && *(p+1) != ']') {
                    p++;
                    c1 = *(p-2) + 1;
                    c2 = *p++;
                    while (c1 <= c2)
                        chset((CHAR)c1++);
                }
#ifdef EXTEND
                else if (*p == '\\' && *(p+1)) {
                    p++;
                    chset(*p++);
                }
#endif
                else
                    chset(*p++);
            }
            if (!*p)
                return badpat("Missing ]");

            for (n = 0; n < BITBLK; bittab[n++] = (char) 0)
                store(mask ^ bittab[n]);
    
            break;

这是在re_comp中进行编译,把正则表达式翻译成中间码。此处,把'[1-9]'翻译成
  '123456789',但你看呀,
如果p指向[,此时,先存储“CCL”,接着,判断
*++p,此时,p指向1,所以下面两个判断不用做,因为不是-,也不是],那就来到了
            while (*p && *p != ']') {
                if (*p == '-' && *(p+1) && *(p+1) != ']') {
                    p++;
                    c1 = *(p-2) + 1;
                    c2 = *p++;
                    while (c1 <= c2)
                        chset((CHAR)c1++);
                }
#ifdef EXTEND
                else if (*p == '\\' && *(p+1)) {
                    p++;
                    chset(*p++);
                }
#endif
                else
                    chset(*p++);
            }
此时,p指向'[1-9]'中的1,所以执行循环中第三分支
                else
                    chset(*p++);
把1保存进去。接着p++,此时p指向-
  那好,接着循环,因为*p=='-'所以执行第一分支
                if (*p == '-' && *(p+1) && *(p+1) != ']') {
                    p++;  //p指向'-',++后指向9
                    c1 = *(p-2) + 1;//c1指向1
                    c2 = *p++;   //p指向9,c2也指向9,但p++后指向]
                    while (c1 <= c2)
                        chset((CHAR)c1++);
                }
此分支,完成的是就是把23456789这几个字符保存好。
  此时,用话不好说,请看代码中注释。这个分支做完后,p指向],因此,要跳出循环。
  做下一步处理
              for (n = 0; n < BITBLK; bittab[n++] = (char) 0)
                store(mask ^ bittab[n]);
也就是把16字节的位置保存到中间代码数组中去。
  此时,p指向],那程序如何处理]的呢?
  程序只有如下分支
case .
case $
case [
case *
case +
....
如果不是这些字符,它是:
        default :               /* an ordinary char  */
            store(CHR);
            store(*p);
            break;
可是,中间代码编译结果中根据没有存储]呀,况且存储了也没意义。你看:
yang@DESKTOP-V9HS3B6:~/grep$ echo "12345" | ./ogrep '[1-9]+'
pattern: [1-9]+
nfacode:
        CCL [123456789]
CLOSURE CCL [123456789]
看到没,正则表达式翻译的结果中根据没有】
我想呀想,不停地翻代码。总是不得劲。
后来,忽然想,不对,这里有个break,就是
case [
   把[1-9]变成ccl 123456789保存到中间码,此时p指向],
   break
结束本次循环后,到哪了?
    for (p = pat; *p; p++) {//此处有p++,哈哈,我明白了。原来在这里!!!
        lp = mp;
        switch(*p) {

        case '.':         
看到我的注释没?p本来指向],这是没错的,但break后,p++,就跳过了]
  通过读程序,发现自己的程序功底还是有欠缺。
同样的错误犯了两次。前一次,看代码
            for (n = 0; n < BITBLK; bittab[n++] = (char) 0)
                store(mask ^ bittab[n]);
这里,是把16字节的位图存储到中间代码结果中去。当时我曾想,你把bittab[]保存进去了,你下次使用是,要初始化吧,那在哪儿初始化呢???
  我找呀找!
  你肯定要初始化的,不然如果这样写正则表达式'[a-z'] hello [1-9]'那如何搞?
  前面使用的位置如果不初始化,后面且不是变成了[a-z1-9]。当时是用kindle读代码,想呀想,后来明白了。
        for (n = 0; n < BITBLK; bittab[n++] = (char) 0)//此处有bittab[n++] = (char)
                store(mask ^ bittab[n]);
作者在for的最后做了。
  代码要经常读,太好了。

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
对于内核源码分析,建议您先了解一些基础知识,例如操作系统的原理、进程管理、内存管理等。同时,了解 C 语言和汇编语言也是必要的。 对于 mount 源码分析,可以参考以下步骤: 1. 阅读 mount 的 man 手册,了解 mount 命令的作用以及参数。 2. 在内核源码中搜索 mount 相关的代码。可以使用 grep 命令,例如: ``` grep -r "do_mount" /path/to/kernel/source ``` 这里以 do_mount 函数为例,它是 mount 命令的核心函数。 3. 了解 do_mount 函数的实现原理。do_mount 函数主要是通过调用 vfs_mount 函数实现挂载的,而 vfs_mount 函数则调用了各个文件系统的 mount 函数来完成实际挂载工作。 4. 深入挖掘各个文件系统的 mount 函数实现,了解它们是如何完成挂载的。这里以 ext4 文件系统为例,可以在 ext4 文件系统源码中找到 mount 函数的实现。 5. 对于一些高级特性,例如 mount namespace,可以在相关的命名空间源码中查找实现。 对于 runc 源码分析,可以参考以下步骤: 1. 了解容器的基本概念和实现原理。例如容器的隔离、命名空间、控制组等。 2. 了解 runc 的作用和实现原理。runc 是一个轻量级容器运行时,它可以创建和管理容器,同时也支持容器的隔离和配置等功能。 3. 在 runc 源码中找到主要的函数和数据结构。例如,可以查找 runc 的 main 函数,了解它是如何解析命令行参数、创建容器和启动容器的。 4. 深入了解 runc 的关键实现。例如,了解 runc 是如何配合 Linux 内核的命名空间和控制组来实现容器的隔离和限制的。 5. 对于一些高级特性,例如容器的网络和存储,可以在相关的代码中查找实现。同时,也可以了解一些容器编排工具,例如 Docker 和 Kubernetes,它们是如何使用 runc 实现容器的。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值