C#文本处理(String)学习笔记

摘要:string是编程中使用最频繁的类型。一个string表示一个恒定不变的字符序列集合。string类型直接继承自object,故他是一个引用类型,也就是说线程的堆栈上不会有任何字符串(直接继承自object的类型一定是引用类型,因为所有的值类型都继承自System.ValueType。值得指出的是System.ValueType是引用类型)。

string是编程中使用最频繁的类型。一个string表示一个恒定不变的字符序列集合。string类型直接继承自object,故他是一个引用类型,也就是说线程的堆栈上不会有任何字符串(直接继承自object的类型一定是引用类型,因为所有的值类型都继承自System.ValueType。值得指出的是System.ValueType是引用类型)。

一、创建字符串

   C#将string认为是基元类型,也就是说编译器也许在源代码中用文本常量来直接表达字符串。编译器会将这些文本常量字符串存放在托管模块的元数据中。在C#中创建string。

  不能通过new操作符来创建string

class Program
{
static void Main(string[] args)
{
string hap = new string("heaiping");
}
}

以上代码会出现编译错误。

相反,应该使用下面的简化语法

class Program
{
static void Main(string[] args)
{
string hap ="heaiping";
}
}

为什么呢,请看下面代码。

代码

namespace StringStudy
{
class Program
{
static void Main(string[] args)
{
string s = "heaiping";//不使用new
SomeClass sc = new SomeClass();//使用new
}
}

class SomeClass
{
}
}

编译以上代码查看ILDasm生成的IL代码如下。

代码

.method private hidebysig static void Main(string[] args) cil managed
{
.entrypoint
// 代码大小 14 (0xe)
.maxstack 1
.locals init ([0] string s,
[1] class StringStudy.SomeClass sc)
IL_0000: nop
IL_0001: ldstr "heaiping"//此处创建string
IL_0006: stloc.0
IL_0007: newobj instance void StringStudy.SomeClass::.ctor()//此处创建SomeClass
IL_000c: stloc.1
IL_000d: ret
} // end of method Program::Main

  如上所见C#对于对象实例的IL指令为newobj,然后调用其构造函数,而对于string,它用了一个特殊的指令ldstr(是不是loadstring,呵呵),该指令通过从元数据中获取文本常量来构造string对象,这表明,CLR有一个更为高效特殊的字符串构造方式。

  string类型也通过了通过new来构造而不是通过文本元数据来构造的构造器,可以接受char*等参数,书上说是为了托管C++提供的,这个就不研究了~~~~~~~

  看下面的代码

class Program
{
static void Main(string[] args)
{
string s = "heaiping";
s = s + " " + "soft";
}
}

  上面的代码使用了+操作符来连接字符串,书上建议不要使用+,因为它会在要执行垃圾回收器的托管堆上创建多个字符串,不是单纯的附在后面。应该使用System.Text.StringBuilder(下次学习);

  还有一种特殊的声明方式,允许编译器将引号之间的字符都认为是字符串的一部分。

代码

class Program
{
static void Main(string[] args)
{
//fileA和fileB是一样的
string fileA = "C://Windows//Temp";
string fileB = @"C:/Windows/Temp";
}
}

@符号会爆速编译器该字符串为一个字面字符串,告知编译器将反斜杠看为字符而不是转义字符。

二、字符串的恒定性

  字符串的恒定性,就是说,一个字符串一旦被创建,就不可能将其变长、变短、或者改变其中任何的字符。

三、字符串比较

罗列一下

成员 成员类型 描述
Compare 静态方法 按照字符串的排序比较,可以控制语言文化,以及是否考虑大小写
CompareTo 实例方法 按照字符串的排序比较,内部使用了当前线程的语言文化
StartsWith/EndsWith 实例方法 是否以指定的字符串开头或者结尾,大小写敏感,内部使用了当前线程的语言文化
CompareOrdinal 静态方法 按照字符集比较,不考虑语言文化,大小写敏感,比较快
Eauals 静态/实例 静态方法比较字符集,实例方法内部调用CompareOrdinal,静态方法首先会检查两个引用是否指向同一个对象,如果是则不再比较字符集
GetHashCode 实例方法 返回列散码

 除以上,string类型还承载了==和!=操作符,其方法内部都是调用string的静态Equals方法实现的 

 对于以上比较有以下说明:

  • 如果是判断字符串是否相等,用CompareOridinal,因为它仅仅是比较字符集是否相等,比较快。
  • 如果是逻辑比较,呈现给用户,何为逻辑比较,虽然有些字符串的字符集不一样,但是逻辑上确实是等的,应该使用Compare方法,Compare内部使用了特定语言文化的排序表。

  在字符串的比较时,语言文化对其影响是很大,Compare方法在内部首先获取与调用线程相关的CurrentCulture,然后读取CurrentCulture的CompareInfo属性。因为CompareInfo对象封装了一个和语言文化相关联的字符比较表,所以每一种文化仅有一个CompareInfo对象。

四、字符串驻留

  字符床的比较是很浪费性能的,CLR通过字符串驻留(string interning)机制来提高性能,看下面代码:

代码

namespace StringStudy
{
class Program
{
static void Main(string[] args)
{
string s = "hap";
Console.WriteLine(object.ReferenceEquals("hap",s));

SomeClass sc = new SomeClass();
Console.WriteLine(object.ReferenceEquals(new SomeClass(), sc));
}
}

class SomeClass
{

}
}

按照引用类型的特性,上面的两个输出都应该为False,因为都是不同的引用在比较,可是  Console.WriteLine(object.ReferenceEquals("hap",s));却输出为True,为什么,难道string不是引用类型吗?? string当然是引用类型,上面的表现由CLR对于string的特殊处理决定的:

  当CLR初始化的时候,它会创建一个内部的散列表,键为字符串,值为指向托管堆中字符串对象的引用。刚开始,该表为空。当JIT编译器编译方法时,它会在散列表中查找每一个文本常量字符串。对于上面的关于string的代码,编译器会首先查找“hap”字符串,并且因为没有找到(第一次还没有),它便就在托管堆上面构造一个新的string对象(指向hap),然后将hap字符串和指向该对象的引用添加到散列表(此时散列表中已经存在键为hap的值),接着JIT编译器在编译器中查找第二个hap字符串,当然此时散列表中已经存在hap,所以不会执行任何操作,代码开始执行。

  当代码执行时,它会在第一行发现需要hap字符串的引用。于是,CLR便在其内部的散列表中查找hap,并且会找到它,这样指向先前创建的string对象的引用就被保存在变量s中。当再往下执行,CLR会再一次在其内部散列表中查找hap,并且仍然会找到,这样子,指向同一个string对象的引用会被传递给object的ReferenceEquals作为第一个参数,当然和第二个参数是同一个引用,自然比较结果为True。

  继续看下面代码:

代码

namespace StringStudy
{
class Program
{
static void Main(string[] args)
{
string s1 = "hap";
string s2 = "h";
string s3 = s2 + "ap";
string s4 = "h" + "ap";
Console.WriteLine(object.ReferenceEquals(s1,s3));//false
Console.WriteLine(s1.Equals(s3));//true

Console.WriteLine(object.ReferenceEquals(s1, s4));//true

}
}
}

这次的输出搞的怪怪的,心里有点毛,到底是怎么回事?原来当一个引用字符串的方法被JIT编译时,所有嵌入在源代码中的文本常量总会被添加到散列表中,s2引用的字符串(h)和一个文本常量字符串(ap)被连接在一起。结果是一个新构造的、位于托管堆中有s3引用的字符串对象,这个动态创建的字符串包含的是一个hap,但是它没有被添加到CLR散列表中,而是另起炉灶,不同的引用所有ReferenceEquals返回fasle,调用Equals相等是因为他们有相同的字符集。

  至于s4=”h"+"ap",是因为IL指令会将两个文本常量字符串连接为一个文本常量字符串hap,所有输出为True。

  ReferenceEquals方法在比较时不需要逐个字符的去比较,只比较引用,效率明显要高于Equals,如果将程序中所有的字符串比较都用引用而不是字符集,那么系统的性能将得到很大的提高。如果有一个方法要是能将含有相同字符集的动态字符串变为托管堆中的一个字符串对象的话,应用程序需要的对象也将更少,从而可以提升系统性能。非常幸运。string类型提高了两个静态方法将可以做到这一点。

public static string Intern(string str);
public static string IsInterned(string str);

  第一个方法Intern接受一个string参数,然后在CLR内部散列表中查找它。如果能够找到该字符串,Intern将返回已经存在的引用。如果找不到,该字符串将被添加到散列表中,Intern最后也会返回它的引用。如果引用程序不再保存作为参数传递的string的引用,垃圾回收器将会回收它。对上面的程序用Intern重新修改:

代码

namespace StringStudy
{
class Program
{
static void Main(string[] args)
{
string s1 = "hap";
string s2 = "h";
string s3 = s2 + "ap";
s3 = string.Intern(s3);
Console.WriteLine(object.ReferenceEquals(s1,s3));//true
Console.WriteLine(s1.Equals(s3));//true
}
}
}

输出全部为true,神奇啊。因为Intern也是需要执行时间的,所以书上建议只有需要多次比较同一个字符串时,才 应该使用字符串驻留技术,否则得不偿失。

  需要注意的是,垃圾回收器不会释放CLR内部散列表中引用的字符串对象。只有当应用程序域都不再应用这些字符串对象时,他们才会被释放。

  IsInterned方法与Intern方法的不同之处是如果在散列表中找不到将会返回null,而不是创建。

代码

namespace StringStudy
{
class Program
{
static void Main(string[] args)
{
string s1 = "heaiping";
string s2 = "h";
string s3 = s2 + "ap";
s3 = string.IsInterned(s3);
if (s3 == null)
{
Console.WriteLine(0);
}
else
{
Console.WriteLine(1);
}

}
}
}

上面程序输出0,因为就当前程序来说散列表中只存在heaiping而不存在hap,所以最后s3变为null。

string还有一些成员Length,IndexOF...Copy...等等。。。不再说了,累了啊。

  

对了以前发过一个关于string的小组讨论http://home.cnblogs.com/group/topic/38270.html

原文如下:

class Class1 

    static void StrChange(string str) 
    { 
      str = "hellow"; 
    }

static void Main() 
    { 
      string str = "123";//申明一个字符串 
      StrChange(str);//调用方法 
      Console.WriteLine(str);//输出字符串 
    } 
}

输出的结果是 "123" 
string 到底是值类型还是引用类型? 
如果是值类型,结果倒还说的过去.但是我记得string 是引用类型啊...难道是我记错了?? 
如果是引用类型的话.输出的结果应该是: "hellow" 
请问这是为什么啊?? 大家帮忙解释一下..谢谢

现在明白了,其实就是与CLR对于string的这个散列表有关,

至于string是特殊的引用类型,个人感觉特殊就特殊在这个散列表上吧

时间: 2024-10-03 13:47:15

C#文本处理(String)学习笔记的相关文章

zepto源码--核心方法5(文本操作)--学习笔记

涉及到文本内容的主要有三个函数:html, text, val. 我们已经见过多次,一个函数多种用途的情况,今天这三个函数也不例外,既可以获取内容,也可以设置内容.判断条件就是有没有传入参数,如果没有传入参数,则表示获取内容,否则表示设置内容.判断条件也基本类似: 0 in arguments,后面不再对该条件判断解析.分两步介绍函数,1为设置内容,2为获取内容. html 获取或设置对象集合中元素的HTML内容. 1.利用javascript原生属性innerHTML获取元素的文本内容(包含h

Python学习笔记基础篇——总览

Python初识与简介[开篇] Python学习笔记——基础篇[第一周]——变量与赋值.用户交互.条件判断.循环控制.数据类型.文本操作 Python学习笔记——基础篇[第二周]——解释器.字符串.列表.字典.主文件判断.对象 Python学习笔记——基础篇1[第三周]——set集合 Python学习笔记——基础篇2[第三周]——计数器.有序字典.元组.单(双)向队列.深浅拷贝.函数.装饰器 Python学习笔记——基础篇[第四周]——迭代器&生成器.装饰器.递归.算法.正则表达式 Python

C++ Primer 学习笔记_73_面向对象编程 --再谈文本查询示例

面向对象编程 --再谈文本查询示例 引言: 扩展第10.6节的文本查询应用程序,使我们的系统可以支持更复杂的查询. 为了说明问题,将用下面的简单小说来运行查询: Alice Emma has long flowing red hair. Her Daddy says when the wind blows through her hair, it looks almost alive, like a fiery bird in flight. A beautiful fiery bird, he

C++ Primer 学习笔记_74_面向对象编程 --再谈文本查询示例[续/习题]

面向对象编程 --再谈文本查询示例[续/习题] //P522 习题15.41 //1 in TextQuery.h #ifndef TEXTQUERY_H_INCLUDED #define TEXTQUERY_H_INCLUDED #include <iostream> #include <fstream> #include <sstream> #include <vector> #include <set> #include <map&g

《Javascript权威指南》学习笔记之十一:处理字符串---String类和正则表达式

一.正则表达式的基本语法 1.概念:正则表达式由普通字符和特殊字符(元字符)组成的文本模式,该模式描述在查找字符串主体时待匹配的一个或者多个字符串.正则表达式作为一个模板,将某个字符模式与所搜索的字符串进行匹配. 普通字符包括所有的大小写字母字符.所有数字.所有标点符号及一些特殊符号.普通字符本身可以组成一个正则表达式,也可以和元字符组合组成一个正则表达式:而元字符则具有特殊的含义,包括().[].{}./.^.$.*.+.?...|.-.?:.?=.?! 2.基本语法 3.优先权含义 二.使用

HTML&CSS基础学习笔记8-预格式文本

<pre>标签的主要作用是预格式化文本.被包围在 pre 标签中的文本通常会保留空格和换行符.而文本也会呈现为等宽字体. <pre>标签的一个常见应用就是用来表示计算机的源代码.当然你也可以在你需要在网页中预显示格式时使用它. 会使你的文本换行的标签(例如<h>.<p>)绝不能包含在 <pre> 所定义的块里.尽管有些浏览器会把段落结束标签解释为简单地换行,但是这种行为在所有浏览器上并不都是一样的. 更多学习内容,就在码芽网http://www.

Perl语言学习笔记 9 正则表达式处理文本

1.替换 s/PATTERN/REPLACE/; #返回是否替换成功的布尔值 可以使用捕获变量,如:s/(\w)/$1/ 匹配失败则不做任何处理 2.定界符 对于没有左右之分的定界符,重复三次即可,如:s///.s### 对于有左右之分的定界符,需使用两对,一对包含模式,一对包含替换字符串,这两对可以不一样,如:s{}{}.s[]{}.s<>[] 3.可选修饰符 /g可进行全局替换,替换所有匹配到的字符串,如:s/ / /g /s:使得 . 匹配所有字符 /i:大小写无关 4.绑定操作符 $f

监测文本输入的字数,终极版,学习笔记

///此方法可以适用多个文本框监测,如果多个文本框同时显示的话须生成多个flag已对应函数中的flag var flag = false; function ff(str) { flag = false; document.getElementById("replycontent" + str).addEventListener("keyup", function () { if (flag == false) { document.getElementById(&

CSS学习笔记:溢出文本省略(text-overflow)

原文:CSS学习笔记:溢出文本省略(text-overflow) 在CSS3中,text-overflow属性的基本语法如下: clip:表示不显示省略文本,简单的裁切. ellipsis:表示对象文本溢出时显示省略标记,省略标记插入的位置是最后一个字符. ellipsis-word:表示当对象文本溢出时显示省略标记,省略标记插入的位置是最后一个词(word). 实际上,text-overflow属性仅用于决定当文本溢出时是否显示省略标记,并不具备样式定义的功能,要实现溢出时产生省略号的效果,应

Linux学习笔记033_8文本处理

less: 一次显示不完是可以向上(n),向下翻页(N),并且可以查找:/keyword,v进入编辑器 cat: 一次显示完文本内容 more: 回车向下翻一行,空格向下翻一页,但是不能向上翻页 nl: 显示行号的输出 head: 显示文件的前10行 head -n 15 a: 显示文件a的前15行 tail: 显示文件的最后10行 tail -n 15 a: 显示文件a的后15行 :查看日志的最后10行 :实时显示最后10行,如果没有-f则为静态 :提取11到20行保存在文件中 grep:(正