1字符串是什么
一段文字就是一排格子,每格放一个字符,格子的编号从 0 开始,叫索引。字符可以存成 char 数组,但更常用 string 关键字;string 是 System. 类的别名,两种写法是同一个类型。下面是顶级语句写法(不写类和 Main,代码从第一行开始执行):
| 1 | string a = "Hello"; | // 用小写 string 声明字符串变量 a |
| 2 | String b = "Hello"; | // 用大写 String(System.String 类)声明 b |
| 3 | Console.WriteLine(typeof(string) == typeof(String)); | // typeof 取出类型,比较两种写法是不是同一个类型:True |
| 4 | Console.WriteLine(typeof(string).FullName); | // .FullName 是类型的全名:System.String |
| 5 | Console.WriteLine(a == b); | // 比较两个变量里的文字是否相同:True |
2创建字符串
教程列了五种办法:直接赋值双引号里的文字、用 String 的构造函数、用 + 连接、读属性或调用返回字符串的方法、用格式化方法。教程示例把它们都用上了:
| 14 | string fullname = fname + lname; | // 用 + 连接:"Rowan" + "Atkinson",中间没有空格 |
| 15 | Console.WriteLine("Full Name: {0}", fullname); | // {0} 处换成 fullname:RowanAtkinson |
查看完整代码(35 行)
| 1 | using System; | // 引入 System 命名空间 |
| 2 | | |
| 3 | namespace StringApplication | // 定义命名空间 StringApplication |
| 4 | { | // StringApplication 命名空间开始 |
| 5 | class Program | // 定义 Program 类 |
| 6 | { | // Program 类开始 |
| 7 | static void Main(string[] args) | // 程序入口 Main 方法 |
| 8 | { | // Main 开始 |
| 9 | //字符串,字符串连接 | |
| 10 | string fname, lname; | // 一次声明两个字符串变量 |
| 11 | fname = "Rowan"; | // 第一种:直接赋值双引号里的文字 |
| 12 | lname = "Atkinson"; | // 给 lname 赋值 |
| 13 | | |
| 14 | string fullname = fname + lname; | // 第三种:用 + 连接,中间没有空格 |
| 15 | Console.WriteLine("Full Name: {0}", fullname); | // 输出 Full Name: RowanAtkinson |
| 16 | | |
| 17 | //通过使用 string 构造函数 | |
| 18 | char[] letters = { 'H', 'e', 'l', 'l','o' }; | // char 数组,5 个字符 |
| 19 | string greetings = new string(letters); | // 第二种:构造函数把字符数组变成字符串 |
| 20 | Console.WriteLine("Greetings: {0}", greetings); | // 输出 Greetings: Hello |
| 21 | | |
| 22 | //方法返回字符串 | |
| 23 | string[] sarray = { "Hello", "From", "Tutorials", "Point" }; | // 4 个单词的字符串数组 |
| 24 | string message = String.Join(" ", sarray); | // 第四种:调用返回字符串的方法 Join |
| 25 | Console.WriteLine("Message: {0}", message); | // 输出 Message: Hello From Tutorials Point |
| 26 | | |
| 27 | //用于转化值的格式化方法 | |
| 28 | DateTime waiting = new DateTime(2012, 10, 10, 17, 58, 1); | // 一个日期时间:2012-10-10 17:58:01 |
| 29 | string chat = String.Format("Message sent at {0:t} on {0:D}", | // 第五种:格式化方法,{0:t} 短时间,{0:D} 长日期 |
| 30 | waiting); | // 两个 {0} 都换成 waiting |
| 31 | Console.WriteLine("Message: {0}", chat); | // 输出格式化后的文字 |
| 32 | Console.ReadKey() ; | // 等待按键(现在的 VS 不写也会停住) |
| 33 | } | // Main 结束 |
| 34 | } | // Program 类结束 |
| 35 | } | // StringApplication 命名空间结束 |
查看完整输出(4 行)
Full Name: RowanAtkinson Greetings: Hello Message: Hello From Tutorials Point Message: Message sent at 17:58 on Wednesday, 10 October 2012
其他几种写法见折叠的完整代码。最后一行的日期写法随电脑的区域设置变:教程的英文日期是英国英语区域设置下的结果,换成中文区域设置就打印中文日期。
| 6 | Console.WriteLine(String.Format(uk, "Message sent at {0:t} on {0:D}", waiting)); | // uk 是英国英语区域设置 en-GB |
| 7 | Console.WriteLine(String.Format(cn, "Message sent at {0:t} on {0:D}", waiting)); | // cn 是中文区域设置 zh-CN |
Message sent at 17:58 on Wednesday, 10 October 2012 Message sent at 17:58 on 2012年10月10日 星期三
3两个属性:Length 和索引
Length 是字符的个数;取某个位置上的字符,在变量后面加方括号写索引,最后一格的索引是 Length - 1。
| 1 | string s = "Hello"; | // 5 个字符,索引 0 到 4 |
| 2 | Console.WriteLine(s.Length); | // 字符个数:5 |
| 3 | Console.WriteLine(s[0]); | // 第 0 格:H |
| 4 | Console.WriteLine(s[4]); | // 第 4 格,也是最后一格(Length - 1):o |
C# 里取字符只能用方括号,写成 s. 会报错(报错里的“程序集”指编译出来的程序文件,比如 .dll):
| 2 | Console.WriteLine(s.Chars(0)); | // 照教程的名字写 Chars |
Program.cs(2,21): error CS1061: “string”未包含“Chars”的定义,并且找不到可接受第一个“string”类型参数的可访问扩展方法“Chars”(是否缺少 using 指令或程序集引用?)
索引写到 5 呢?编译能通过,一运行就抛出越界异常 System.,索引只能是 0 到 Length - 1。
| 2 | Console.WriteLine(s[5]); | // "Hello" 只有 0 到 4,5 越界了 |
4不可变
想把第 0 格的 H 换成 J 也不行:
| 2 | s[0] = 'J'; | // 想改第 0 格:方括号取字符只能读,不能写 |
编译器报 error CS0200: 无法为属性或索引器“string.。字符串一旦创建,里面的字符就不能再改,这叫不可变。
视频里用锁杆压住字模来比喻;真实的字符串是内存里一段连续的字符编码,没有槽也没有锁。
5方法分六组
String 类的方法很多,教程的表列了 36 行,按用途分成六组:比较、连接、查找、生成新字符串、拆分、其他。先记住一点:这些方法都不改原来的字符串,要得到改过的文字,都是返回一个新字符串。
6比较
Compare 比较两个字符串在排序里谁前谁后,返回的是整数,不是 true/false;默认区分大小写,第三个参数写 true 就忽略大小写。Equals 只回答一样不一样。
| 1 | string a = "apple", b = "Apple"; | // a 是小写 apple,b 是大写开头的 Apple |
| 2 | Console.WriteLine(String.Compare("apple", "banana")); | // apple 排在前面:-1 |
| 3 | Console.WriteLine(String.Compare("banana", "apple")); | // 反过来比:1 |
| 4 | Console.WriteLine(String.Compare(a, b)); | // 默认区分大小写,结果不是 0:-1 |
| 5 | Console.WriteLine(String.Compare(a, b, true)); | // 第三个参数 true:忽略大小写,0 表示相等 |
| 6 | Console.WriteLine(a.Equals(b)); | // Equals 区分大小写:False |
| 7 | Console.WriteLine(String.Equals(a, "apple")); | // 两个参数的写法:True |
教程的比较示例里,"This is test" 和 "This is text" 只差一个字母,Compare 结果不是 0,所以打印 This is test and This is text are not equal.。
7连接
Concat 把两个、三个或四个字符串首尾接起来;Join 多一个分隔符,把数组的每个元素用它隔开再拼起来,还能只取数组的一段。
| 1 | Console.WriteLine(String.Concat("C", "#")); | // 两个接起来:C# |
| 2 | Console.WriteLine(String.Concat("Hello", " ", "World")); | // 三个接起来:Hello World |
| 3 | Console.WriteLine(String.Concat("a", "b", "c", "d")); | // 四个接起来:abcd |
| 4 | string[] colors = { "red", "green", "blue", "black" }; | // 4 个颜色的数组 |
| 5 | Console.WriteLine(String.Join(", ", colors)); | // 用逗号加空格隔开:red, green, blue, black |
| 6 | Console.WriteLine(String.Join("-", colors, 1, 2)); | // 从索引 1 开始取 2 个,用 - 连:green-blue |
教程的连接示例用换行 "\n" 当分隔符,五句歌词就各占一行:
| 15 | string str = String.Join("\n", starray); | // \n 表示换行,每个元素之间插一个换行 |
8查找
拿 banana 来试:6 个字符,索引 0 到 5。Contains("nan")、StartsWith("ba")、EndsWith("na") 分别问“里面有没有”“开头是不是”“结尾是不是”,实测都是 True。教程示例里 str. 找到了 test,就打印 The sequence 'test' was found.。
IndexOf 返回第一次出现的位置,第二个参数是起点,找不到返回 -1:
| 2 | Console.WriteLine(s.IndexOf('a')); | // 第一个 a 在索引 1 |
| 3 | Console.WriteLine(s.IndexOf("na")); | // 找字符串,返回它开头的位置:2 |
| 4 | Console.WriteLine(s.IndexOf('a', 2)); | // 从索引 2 开始找 a:3 |
| 5 | Console.WriteLine(s.IndexOf("na", 3)); | // 从索引 3 开始找 na:4 |
| 6 | Console.WriteLine(s.IndexOf('z')); | // 没有 z:-1 |
IndexOfAny 给一组字符,谁先出现就停在哪:找 { 'n', 'x' } 时 n 先出现,结果是 2;LastIndexOf 找最后一次出现的位置:s. 是 5。
9生成新字符串
拿 Hello World 来试:每个方法都返回一个新字符串,最后打印 s 本身,一个字都没变。
| 1 | string s = "Hello World"; | // 原字符串,索引 0 到 10 |
| 2 | Console.WriteLine(s.Insert(5, ",")); | // 在索引 5 插进逗号:Hello, World |
| 3 | Console.WriteLine(s.Remove(5)); | // 从索引 5 删到最后:Hello |
| 4 | Console.WriteLine(s.Remove(0, 6)); | // 从索引 0 开始删 6 个:World |
| 5 | Console.WriteLine(s.Replace('o', '0')); | // 把所有字符 o 换成数字 0:Hell0 W0rld |
| 6 | Console.WriteLine(s.Replace("World", "C#")); | // 把字符串 World 换成 C#:Hello C# |
| 7 | Console.WriteLine(s); | // s 本身没变:Hello World |
Remove 不会动原来的字符串,返回的是一个新字符串,上面最后一行就是证据。ToUpper、ToLower 变大写、小写,Trim 去掉开头和结尾的空白:
| 4 | string t = " Hi "; | // 前后各有 3 个空格 |
| 5 | Console.WriteLine("[" + t + "]"); | // 加中括号看清空格:[ Hi ] |
| 6 | Console.WriteLine("[" + t.Trim() + "]"); | // Trim 去掉开头和结尾的空白:[Hi] |
很多人以为单独写一句 s. 就把 s 改成大写了,实测还是小写;要用新结果,得赋值回给 s:
| 1 | string s = "hello"; | // 原字符串是小写 |
| 2 | s.ToUpper(); | // 返回了新字符串 HELLO,但没人接住,就丢了 |
| 3 | Console.WriteLine(s); | // s 没变:hello |
| 4 | s = s.ToUpper(); | // 把新字符串赋值回给 s |
| 5 | Console.WriteLine(s); | // 这回是 HELLO |
10拆分和字符数组
Split 按分隔符切开,返回字符串数组;ToCharArray 拆成字符数组;CopyTo 把一段字符复制进已有的数组。
| 1 | string csv = "red,green;blue"; | // 用逗号和分号隔开的三段 |
| 2 | string[] parts = csv.Split(',', ';'); | // 逗号和分号都算分隔符 |
| 3 | Console.WriteLine(parts.Length); | // 切出 3 段 |
| 4 | Console.WriteLine(String.Join(" | ", parts)); | // 用 | 连起来看清每一段:red | green | blue |
| 5 | string[] two = csv.Split(new char[] { ',', ';' }, 2); | // 数量 2:最多切两段 |
| 6 | Console.WriteLine(String.Join(" | ", two)); | // 剩下的都留在第二段:red | green;blue |
"Hello". 拆出 5 个字符,ToCharArray( 从索引 1 开始拆 3 个,得到 ell。CopyTo 的例子:
| 5 | char[] dest = { '*', '*', '*', '*', '*' }; | // 一个装着 5 个 * 的数组 |
| 6 | "Hello".CopyTo(1, dest, 0, 3); | // 从 Hello 的索引 1 复制 3 个字符,放进 dest 的 0 号位置起 |
| 7 | Console.WriteLine(new string(dest)); | // 前 3 格被换成 ell,后 2 格还是 *:ell** |
11其他方法
String. 现在已经被标成过时,编译会给警告 CS0618;字符串本来就不可变,直接赋值共用就行。Format 把 {0} 换成后面参数的文字(a 是 "abc" 时,String. 得到“长度是 3”),IsNullOrEmpty 判断是不是空值(null)或空字符串。
| 5 | Console.WriteLine(String.IsNullOrEmpty("")); | // 空字符串:True |
| 6 | Console.WriteLine(String.IsNullOrEmpty(null)); | // null 表示什么都没有:True |
| 7 | Console.WriteLine(String.IsNullOrEmpty(" ")); | // 有一个空格,不算空:False |
完整的方法列表查微软的 Microsoft Learn 文档(教程里的 MSDN 已改名)。
12取子字符串
教程还有一个示例用到 Substring,表里没列,但很常用:
| 8 | string str = "Last night I dreamt of San Pedro"; | // 原字符串 |
| 9 | Console.WriteLine(str); | // 先原样输出 |
| 10 | string substr = str.Substring(23); | // 从索引 23(S 那一格)一直取到最后 |
| 11 | Console.WriteLine(substr); | // 得到最后两个词:San Pedro |
13小结
- 字符串是一排字符,索引从 0 开始,
Length是个数,最后一格是Length - 1。 - 字符串不可变,方法都返回新的字符串,原来的不动;要用结果就接住它。
下一节:想把一个学生的名字、年龄、成绩打包成一个值,怎么办?
