• String.Trim()真相大揭秘


    部门上次的CodeReview会议上,留下了一道作业题,也是本篇博文所要讲述的:String.Trim()方法到底为我们做了什么,仅仅是去除字符串两端的空格吗?

    很久很久以前,也就是前几个小时前,我一直以为Trim()方法就是把字符串两端的空格字符给删去,其实我错了,而且错的比较离谱。

    首先我们直接用Relector反编译String类,找到Trim()方法:

    public string Trim()
    {
        return this.TrimHelper(WhitespaceChars, 2);
    }

    (旁白:Relector工具真是好用啊,哈哈)

    看到这里,可能会感到很奇特(我也是),Trim()方法不就是去掉两端的空格吗?怎么还出现了一个TrimHelper方法?

    TrimHelper方法有两个参数,第一个参数名WhitespaceChars,首字母尽然是大写的,肯定有文章,真不出我所料:

    internal static readonly char[] WhitespaceChars;

    这里只是定义它,没有赋值,而且是静态的,我们看看构造函数去,果然找到:

    static String()
    {
        Empty = " ";
        WhitespaceChars = new char[] { 
            '\t', '\n', '\v', '\f', '\r', ' ', '\x0085', '\x00a0', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', 
            ' ', ' ', ' ', ' ', '​', '\u2028', '\u2029', ' ', ''
         };
    }

    WhitespaceChars是在String的静态构造函数中赋值的。
    这篇文章是在记事本里面写的(Word 07打开的那个速度,实在是不敢恭维),当我将上一段代码从Reflector拷贝到记事本中,出现了变化(看下图):

    trim001

    Trim方法就是把字符串两端的这些字符给删去?我很坚定的猜想到。

    继续我们的探索,直接反编译TrimHelper,哇,也许这个才是我想要的,私有的TrimHelper方法:

    private string TrimHelper(char[] trimChars, int trimType)
    {
        int num = this.Length - 1;
        int startIndex = 0;
        if (trimType != 1)
        {
            startIndex = 0;
            while (startIndex < this.Length)
            {
                int index = 0;
                char ch = this[startIndex];
                index = 0;
                while (index < trimChars.Length)
                {
                    if (trimChars[index] == ch)
                    {
                        break;
                    }
                    index++;
                }
                if (index == trimChars.Length)
                {
                    break;
                }
                startIndex++;
            }
        }
        if (trimType != 0)
        {
            num = this.Length - 1;
            while (num >= startIndex)
            {
                int num4 = 0;
                char ch2 = this[num];
                num4 = 0;
                while (num4 < trimChars.Length)
                {
                    if (trimChars[num4] == ch2)
                    {
                        break;
                    }
                    num4++;
                }
                if (num4 == trimChars.Length)
                {
                    break;
                }
                num--;
            }
        }
        int length = (num - startIndex) + 1;
        if (length == this.Length)
        {
            return this;
        }
        if (length == 0)
        {
            return Empty;
        }
        return this.InternalSubString(startIndex, length, false);
    }

    经过我大脑CPU的精确的分析和运行,基本上知道了这个方法是干什么的了。

    TrimHelper方法有两个参数:

    第一个参数trimChars,是要从字符串两端删除掉的字符的数组;

    第二个参数trimType,是标识Trim的类型。就目前发现,trimType的取值有3个。当传入0时,去除字符串头部的空白字符,传入1时去除字符串尾部的空白字符,传入其他数值(比如2)去除字符串两端的空白字符。

    最后再看看真正执行字符串截取的方法:

    private unsafe string InternalSubString(int startIndex, int length, bool fAlwaysCopy)
    {
        if (((startIndex == 0) && (length == this.Length)) && !fAlwaysCopy)
        {
            return this;
        }
        string str = FastAllocateString(length);
        fixed (char* chRef = &str.m_firstChar)
        {
            fixed (char* chRef2 = &this.m_firstChar)
            {
                wstrcpy(chRef, chRef2 + startIndex, length);
            }
        }
        return str;
    }

    原来微软也用指针的?第一次看到,效率应该比较高吧。
    最后总结一下:
    String.Trim()方法会去除字符串两端,不仅仅是空格字符,它总共能去除25种字符:
    ('\t', '\n', '\v', '\f', '\r', ' ', '\x0085', '\x00a0', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', '​', '\u2028', '\u2029', ' ', '')
    如果你想保留其中的一个或多个(例如\t制表符,\n换行),请慎用Trim方法。

    请注意,Trim删除的过程为从外到内,直到碰到一个非空白的字符为止,所以不管前后有多少个连续的空白字符都会被删除掉。

    最后附上两个相关的方法(也是String类直接提供的),分别去除字符串头部空白字符的TrimStart方法和去除字符串尾部空白字符的TrimEnd方法:

    TrimStart和TrimEnd方法

    的确验证了我上面CPU分析的结果,嘎嘎。

    如果想去除字符串两端其他任意字符,可以考虑Trim他的重载兄弟:String.Trim(Char[]),传入你想要去除的哪些字符的数组。

    源码奉上:

    public string Trim(params char[] trimChars)
    {
        if ((trimChars == null) || (trimChars.Length == 0))
        {
            trimChars = WhitespaceChars;
        }
        return this.TrimHelper(trimChars, 2);
    }

    空格 != 空白字符,删除空格请使用: Trim(‘ ‘);

    最后给各位大大留个思考题:

    string s = " from dual union all ";
    s = s.Trim().TrimEnd("union all".ToCharArray());

    请问最终s的值是什么?哈哈(刚在另一个人大大的博客上看到这道题目的)

    博客园牛人巨多的噢噢。

  • 相关阅读:
    php 数组去重
    投票 页的做法 重点——学会进度条!!
    封装 类
    HPH-——>mysql 批量删除
    php->msql 多条件查询
    php-> msql 修改
    PHP ->masql 登录 增 删 改
    php 连接数据库
    Python 第十七章 序列化+os+sys+hashlib+collections
    Python 第十六章 自定义模块+time+datetime+random
  • 原文地址:https://www.cnblogs.com/aaa6818162/p/1646778.html
Copyright © 2020-2023  润新知