ruby049源码分析_string.c文件

57 篇文章 0 订阅
21 篇文章 0 订阅

ruby049源码分析_string.c文件
昨天写了array.c的文件中各函数的解读,下班后,在床上就进行回顾。发现真正写一遍后,自己思维更清楚了。就是说,象一本书《59秒》所言,倾说无益,书写才有效果。把知识点自己写下来,自己理解得更深入了。
所以今天接着来分析string.c文件,因为字符串我使用得也较多,算是稍微熟悉一些。
一、初始化函数
先看结构体
struct RString {
    struct RBasic basic;
    UINT len;
    char *ptr;
    struct RString *orig;
};
其中len存储字串的长度,*ptr指向字串的具体位置,这两个都好理解,只有orig指针不懂。感觉从心理上无法理解。
好像awk中,字串的定义也比较怪。
    struct {
        char *sp;
        short slen,sref;
    } str;
    AWKNUM fltnum;
  } sub;
} NODE;
我把awk.h文件中,关于字串的定义弄出来。其中sref什么意思?我不明白。不想了。看函数吧。

VALUE
str_new(ptr, len)
    char *ptr;
    UINT len;
{
    NEWOBJ(str, struct RString);
    OBJSETUP(str, C_String, T_STRING);

    str->len = len;
    str->ptr = ALLOC_N(char,len+1);
    if (ptr) {
    memmove(str->ptr, ptr, len);
    }
    str->ptr[len] = '\0';
    str->orig = Qnil;
    return (VALUE)str;
}
函数写得中规中矩。用字串ptr[0..len-1]进行初始化。其中注意分配空间时要加1.如下所示:
    str->ptr = ALLOC_N(char,len+1);

下面的函数就用到了orig分量。
VALUE
str_new3(str)
    struct RString *str;
{
    NEWOBJ(str2, struct RString);
    OBJSETUP(str2, C_String, T_STRING);

    str2->len = str->len;
    str2->ptr = str->ptr;
    str2->orig = str;

    return (VALUE)str2;
}
但是,并没有真正的为str2分配空间,而是让str2->orig指向已经有的str,这是什么意思?不明白。

static ID pr_str = Qnil;

VALUE
obj_as_string(obj)
    VALUE obj;
{
    VALUE str;

    if (TYPE(obj) == T_STRING) {
    return obj;
    }
    str = rb_funcall(obj, pr_str, 0);
    if (TYPE(str) != T_STRING)
    return Fkrn_to_s(obj);
    return str;
}
这个函数好象是把某个对象转换成字串对象,以便于打印。

VALUE
Fstr_clone(str)
    struct RString *str;
{
    VALUE obj;

    if (str->orig)
    obj = str_new3(str->orig);
    else
    obj = str_new(str->ptr, str->len);
    CLONESETUP(obj, str);
    return obj;
}
这个函数的用法 是进行对象clone,但如果orig不空,只是浅复制。如果orig空,就真正的产生字串对象。


static VALUE
Fstr_new(class, str)
    VALUE class;
    struct RString *str;
{
    Check_Type(str, T_STRING);
    {
    NEWOBJ(str2, struct RString);
    OBJSETUP(str2, class, T_STRING);

    str2->len = str->len;
    str2->ptr = ALLOC_N(char, str->len+1);
    if (str2->ptr) {
        memmove(str2->ptr, str->ptr, str->len);
    }
    str2->ptr[str->len] = '\0';
    str2->orig = Qnil;
    return (VALUE)str2;
    }
}
也是字串初始化的操作。但不是和最开始的一样功能吗?我想,或许在s=String.new("okok")之类操作中使用吗?不明白其使用场景。


static VALUE
Fstr_length(str)
    struct RString *str;
{
    return INT2FIX(str->len);
}
这个函数计算字串的长度。我记得C语言需要用库函数来能得到字串长度。ruby直接可以取字串长度。省事了。


VALUE
Fstr_plus(str1, str2)
    struct RString *str1, *str2;
{
    struct RString *str3;

    GC_LINK;
    GC_PRO3(str2, as_str(str2));
    str3 = (struct RString*)str_new(0, str1->len+str2->len);
    memcpy(str3->ptr, str1->ptr, str1->len);
    memcpy(str3->ptr+str1->len, str2->ptr, str2->len);
    str3->ptr[str3->len] = '\0';
    GC_UNLINK;

    return (VALUE)str3;
}
函数实现功能是:s1="abc";s2="def"
s1+s2结果是"abcdef"
matz用mem*函数,而没有用for循环。很好。


VALUE
Fstr_times(str, times)
    struct RString *str;
    VALUE times;
{
    struct RString *str2;
    int i;

    times = NUM2INT(times);

    str2 = (struct RString*)str_new(0, str->len*times);
    for (i=0; i<times; i++) {
    memmove(str2->ptr+(i*str->len), str->ptr, str->len);
    }
    str2->ptr[str2->len] = '\0';

    return (VALUE)str2;
}
该函数实现功能是:s1="abc";
s1*3结果是"abcabcabc"
matz的每个函数都能在一屏内显示出来,很爽。

 

VALUE
str_substr(str, start, len)
    struct RString *str;
    int start, len;
{
    struct RString *str2;

    if (start < 0) {
    start = str->len + start;
    }
    if (str->len <= start) {
    Fail("index %d out of range [0..%d]", start, str->len-1);
    }
    if (len < 0) {
    Fail("Negative length %d", len);
    }

    str2 = (struct RString*)str_new(str->ptr+start, len);

    return (VALUE)str2;
}
求字串的子串。如s1="abcdefg"
s1[2,3]结果为"cde"
作者没有对len的长度进行限制。


VALUE
str_subseq(str, beg, end)
    struct RString *str;
    int beg, end;
{
    int len;

    if (beg < 0) {
    beg = str->len + beg;
    if (beg < 0) beg = 0;
    }
    if (end < 0) {
    end = str->len + end;
    if (end < 0) end = 0;
    }

    if (beg > end) {
    int tmp;

    if (verbose) {
        Warning("start %d is bigger than end %d", beg, end);
    }
    tmp = beg; beg = end; end = tmp;
    }

    if (beg >= str->len) {
    return str_new(0, 0);
    }
    if (str->len < end) {
    end = str->len;
    }

    len = end - beg + 1;
    if (len < 0) {
    Fail("end %d too small(size %d)", end, str->len);
    }

    return str_substr(str, beg, len);
}
这个函数再调用 str_substr,并对len进行了处理。当ben+len超出str的末尾时,进行了处理。

void
str_modify(str)
    struct RString *str;
{
    if (str->orig == Qnil) return;
    str->ptr = ALLOC_N(char, str->len+1);
    if (str->ptr) {
    memcpy(str->ptr, str->orig->ptr, str->len+1);
    }
    str->orig = Qnil;
}
这个函数可有些莫其妙了。你看。用orig中的字串内容恢复,orig中的内容使用过后,就变为nil,什么意思,备份只能使用一次?不懂。
我实在受不了,没有缩进代码不好看,于是在vimrc文件中进行修改。
set tabstop=8 " 1个Tab占4个空格  
这下代码舒服多了。

VALUE
str_grow(str, len)
    struct RString *str;
    UINT len;
{
    str_modify(str);
    if (len > 0) {
    REALLOC_N(str->ptr, char, len + 1);
    str->len = len;
    str->ptr[len] = '\0';    /* sentinel */
    }
    return (VALUE)str;
}
不过,字串的空间进行扩大,按传进来的大小加1后,再分配空间。

VALUE
str_cat(str, ptr, len)
    struct RString *str;
    char *ptr;
    UINT len;
{
    str_modify(str);

    if (len > 0) {
    REALLOC_N(str->ptr, char, str->len + len + 1);
    if (ptr)
        memmove(str->ptr + str->len, ptr, len);
    str->len += len;
    str->ptr[str->len] = '\0'; /* sentinel */
    }
    return (VALUE)str;
}
实现的功能 是:s="abc";s1="def"
s.strcat(s1)结果是"abcdef"
就是把字串s1追加到s后面。还是使用memmove复制。

static char
str_next(s)
    char *s;
{
    char c = *s;

    /* control code */
    if (c < ' ') return 0;

    /* numerics */
    if ('0' <= c && c < '9') (*s)++;
    else if (c == '9') {
    *s = '0';
    return '1';
    }
    /* small alphabets */
    else if ('a' <= c && c < 'z') (*s)++;
    else if (c == 'z') {
    return *s = 'a';
    }
    /* capital alphabets */
    else if ('A' <= c && c < 'Z') (*s)++;
    else if (c == 'Z') {
    return *s = 'A';
    }
    return 0;
}
这个函数是计算字符的下一字符。如
'a'的next是'b','z'的next是'aa'
函数中有两个地方有传出的值,一个是参数,一个是函数的返回值。如
c='z';
d=str(&c);
此时d=='a',c=='a'
合起来,就是'z' 的后继为'aa'
static VALUE
Fstr_next(orig)
    struct RString *orig;
{
    struct RString *str, *str2;
    char *sbeg, *s;
    char c = -1;

    GC_LINK;
    GC_PRO3(str, (struct RString*)str_new(orig->ptr, orig->len));
    
    sbeg = str->ptr; s = sbeg + str->len - 1;

    while (sbeg <= s) {
    if (isalnum(*s) && (c = str_next(s)) == Qnil) break;
    s--;
    }
    if (s < sbeg && c != -1) {
    GC_PRO3(str2, (struct RString*)str_new(0, str->len+1));
    str2->ptr[0] = c;
    memmove(str2->ptr+1, str->ptr, str->len);
    obj_free(str);
    str = str2;
    }
    GC_UNLINK;

    return (VALUE)str;
}

这个函数写得很巧妙。因为我知道"z".next是"aa",一定要考虑到函数str_next的返回值
我想,要慢慢的回味。
    while (sbeg <= s) {
    if (isalnum(*s) && (c = str_next(s)) == Qnil) break;
    s--;
    }
的功能是遇到字符数字就进行递增。但c的返回值要么为0要么为其它字符,为什么后面要判断为-1呢?
    if (s < sbeg && c != -1) {
    GC_PRO3(str2, (struct RString*)str_new(0, str->len+1));
    str2->ptr[0] = c;
    memmove(str2->ptr+1, str->ptr, str->len);
    obj_free(str);
    str = str2;
    }
上面一句:
    if (s < sbeg && c != -1) {
c肯定不会和-1相等呀。                                      
我要慢慢调试了。
当然作者的意思是先给c的初始值是-1,但只要函数str_next调用了,返回值要么是0,要么是字符。不可能是-1的,所以这个条件是可以去掉的。

static
str_hash(str)
    struct RString *str;
{
    int len = str->len;
    unsigned char *p = (unsigned char*)str->ptr;
    int key = 0;

    if (ignorecase) {
    while (len--) {
        key = key*65599 + *p;
    }
    }
    else {
    while (len--) {
        key = key*65599 + toupper(*p);
    }
    }
    return key;
}
我曾经分析过,此处应该对p指针递增,所以这里有一个bug.


int
str_cmp(str1, str2)
    struct RString *str1, *str2;
{
    UINT len;
    int retval;

    if (ignorecase != Qnil) {
    return str_cicmp(str1, str2);
    }

    len = min(str1->len, str2->len);
    retval = memcmp(str1->ptr, str2->ptr, len);
    if (retval == 0) {
    return str1->ptr[len] - str2->ptr[len];
    }
    return retval;
}
判断两个字串是否相等。

 

static VALUE
Fstr_match(this, other)
    struct RString *this, *other;
{
    VALUE reg;
    int start;

    switch (TYPE(other)) {
      case T_REGEXP:
    return Freg_match(other, this);
      case T_STRING:
    reg = re_regcomp(other);
    start = research(reg, this, 0, ignorecase);
    if (start == -1) {
        return FALSE;
    }
    return INT2FIX(start);
      default:
    Fail("type mismatch");
    break;
    }
}
很惭愧,几个正则表达式相关的函数没弄明白。下次再深入。


static int
str_index(str, sub, offset)
    struct RString *str, *sub;
    int offset;
{
    char *s, *e, *p;
    int len;

    if (str->len - offset < sub->len) return -1;
    s = str->ptr+offset;
    p = sub->ptr;
    len = sub->len;
    e = s + str->len - len + 1;
    while (s < e) {
    if (*s == *(sub->ptr) && memcmp(s, p, len) == 0) {
        return (s-(str->ptr));
    }
    s++;
    }
    return -1;
}
函数实现功能是:s1="abcdbc",s1.index("bc",0)结果是1,
s1.index("bc",2)结果是4,
字串函数还有许多。下次再写吧。

 

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值