C# 数据类型之 String(字符串)
Ø 简介
在开发中最常见的数据类型就是 String 类型,即字符串类型。为什么要单独讨论下这个类型,是因为:它是系统内置的基础数据类型;它的使用频率非常高;它是一个特殊的引用类型。其实大家都会使用它,但可能或多或少了解不够全面,本文主要是抱着:学习、巩固、总结的目的去加深对它的了解,主要学习以下几点:
1. 什么是 string 类型
2. 创建 string 对象的方式
3. string 的常用静态方法
4. string 的常用实例方法
5. string 的常用扩展方法
6. string 的使用技巧
7. char 的常用静态方法
1. 什么是 string 类型
1) 它主要用于存储一组连续字符(char),一个完整的字符串对象可以看作是一个 char 类型的数组,既然是数组我们就可以把它当作集合去使用,例如:
string str1 = "abc";
char[] chars = new char[str1.Length];
//方式1
for (int i = 0; i < chars.Length; i++)
chars[i] = str1[i];
//方式2
chars = str1.ToArray();
2) 它是一种特殊的引用类型
1. 在 MSDN 上,string 就被分为引用类型,https://msdn.microsoft.com/zh-cn/library/t63sy5hs(VS.80).aspx。
另外,从声明上也可以看出,它是一个密封的 class:
public sealed class String : IComparable, ICloneable, IConvertible, IComparable<string>, IEnumerable<char>, IEnumerable, IEquatable<string>
注:可见,string 实现了 IEnumerable<char> 接口,所以 string 也可以使用很多 IEnumerable<char> 接口的扩展方法。
2. string 的值具有不变性
string 最显著的特点就是它具有恒定不变性,我们一旦创建了一个 string 对象,就不能以任何方式改变该对象的值,比如:追加、删减、改变格式等。有时看上去改变了某个字符串,但实际上则是创建了一个新的字符串实例。
3. 为什么又说它是一种特殊的引用类型呢,是因为它是引用类型,有时却表现出值类型的特点,例如:
1) 示例一
string str2 = "abc";
string str3 = str2;
str2 = "def";
Console.WriteLine("str2:{0}, str3:{1}", str2, str3); //str2:def, str3:abc
首先,将"abc"存储在内存中,并将引用地址赋给 str2;然后,将 str2 的引用赋给 str3,此时 str2 与 str3 具有相同的引用;最后,改变了 str2 的值(注意是新值并不是改变),此时 str2 与 str3 并不是引用同一个内存地址。所以,最终输出并不相同。(个人理解)
2) 示例二(以上示例解释通了,下面的示例又做如何解释)
StringTestOne stringTestOne1 = new StringTestOne() { Name = "爱变成" };
StringTestOne stringTestOne2 = stringTestOne1;
stringTestOne2.Name = "张无忌";
Console.WriteLine("One1.Name:{0}, One2.Name:{1}", stringTestOne1.Name, stringTestOne2.Name); //One1.Name:张无忌, One2.Name:张无忌
首先,创建了一个包含 Name 属性的对象;然后,将该对象的引用赋值给 stringTestOne2;之后,再改变了 stringTestOne2.Name 的值;结果,输出两个对象的 Name 属性,尽然是一样的值。
这样是不是有点蒙了,为什么跟上面的示例不太一样呢?我们先看引用情况:
bool bool1_1 = object.ReferenceEquals(stringTestOne1, stringTestOne2); //true
bool bool1_2 = object.ReferenceEquals(stringTestOne1.Name, stringTestOne2.Name); //true
没错,它们是相同的对象引用,无论是对象还是属性,都是同一个引用地址。
至此,再画张图解释一下:
2. 创建 string 对象的方式
1) 直接赋值
string str5 = "abc"; //在堆中创建对象,并将引用分配给变量 str5。
2) 调用 string 的构造函数(+ 7 重载)
string str6 = new string(new char[] { 'a', 'b', 'c' });
3. string 的常用静态方法
1) Compare() 方法(+ 10 重载),比较两个字符串。示例:
int int1 = string.Compare("acd", "abc"); //1
int int2 = string.Compare("123", "135"); //-1
int int3 = string.Compare("成", "成"); //0
2) Concat() 方法(+ 10 重载),连接(串联)每个参数。示例:
string str7 = string.Concat("abc", true, 3); //abcTrue3
3) Join () 方法(+ 4 重载),使用指定的分隔符,连接(串联)每个参数。示例:
string str8 = string.Join(",", new int[] { 1, 2, 3 }); //1,2,3
string str8_1 = string.Join(",", new int[] { 1 }); //1
string str8_2 = string.Join(",", new int[] { }); //""
string str8_3 = string.Join(",", null); //System.ArgumentNullException:值不能为 null。
4) Copy() 方法,将指定的字符串的值拷贝到新的字符串。示例:
string str9 = "abc123"; //abc123
string str10 = str9; //abc123
string str11 = string.Copy(str9); //abc123
bool bool1 = str9.Equals(str10); //true
bool bool2 = str9.Equals(str11); //true
bool bool3 = object.ReferenceEquals(str9, str10); //true
bool bool4 = object.ReferenceEquals(str9, str11); //false
注:可见,使用 Copy() 方法拷贝值的方式,与原对象的引用不同。
5) Equals() 方法(+ 2 重载),比较两个字符串的值是否相等。示例:
string str12 = "abc123";
string str13 = "abc123";
bool bool5 = string.Equals(str12, str13); //true
bool bool6 = object.ReferenceEquals(str12, str13); //true
bool bool6_1 = string.Equals("a", "A", StringComparison.CurrentCulture); //false
bool bool6_2 = string.Equals("a", "A", StringComparison.CurrentCultureIgnoreCase); //true
注:可见,相同值的字符串对象并不会重复创建,这个很关键!
6) Format() 方法(+ 4 重载),根据指定的格式格式化字符串。示例:
string str14 = string.Format("格式化{0}", "字符串"); //格式化字符串
//输出字符串包含“{”时,需要使用"{{"转义
string str15 = string.Format("{{格式化{0}", "字符串"); //{格式化字符串
//string str16_1 = string.Format("{0}{1}", "a"); //System.FormatException:索引(从零开始)必须大于或等于零,且小于参数列表的大小。
string str16_2 = string.Format("{0}{1}", "a", "b", "c"); //ab(注:可见,参数列表可多不可少,多余的则忽略。)
1. 去除小数点后面无效的零
string str16_3 = string.Format("{0:0.##}", 16.00m); //16
string str16_4 = string.Format("{0:0.##}", 16.10m); //16.1
string str16_5 = string.Format("{0:0.##}", 16.06m); //16.06
string str16_6 = string.Format("{0:0.##}", 16.160m); //16.16
string str16_7 = string.Format("{0:0.##}", 16.164m); //16.16
string str16_8 = string.Format("{0:0.##}", 16.165m); //16.17(注:进行了四舍五入)
7) IsNullOrEmpty() 方法,检查指定的字符串是否为 string.Empty 或 null。示例:
bool bool7 = string.IsNullOrEmpty(""); //true
bool bool8 = string.IsNullOrEmpty(null); //true
bool bool9 = string.IsNullOrEmpty(" "); //false
8) IsNullOrEmpty() 方法,检查指定的字符串是否为 string.Empty、null 或仅由空白字符组成。示例:
bool bool10 = string.IsNullOrWhiteSpace(""); //true
bool bool11 = string.IsNullOrWhiteSpace(null); //true
bool bool12 = string.IsNullOrWhiteSpace(" "); //true
bool bool13 = string.IsNullOrWhiteSpace("1"); //false
9) CompareOrdinal() 方法,通过计算每个字符串中相应 System.Char 对象的数值来比较两个指定的 System.String 对象。示例:
int int3_1 = string.CompareOrdinal("abc123", "abc123"); //0
int int3_2 = string.CompareOrdinal("abc123", "def123"); //-3
int int3_3 = string.CompareOrdinal("dec123", "abc123"); //3
4. string 的常用实例方法
1) CompareTo() 方法(+ 2 重载),比较两个字符串。示例:
int int4 = "acd".CompareTo("abc"); //1
int int5 = "123".CompareTo("135"); //-1
int int6 = "成".CompareTo("成"); //0
注:该方法与静态方法 CompareTo() 类似。
2) Contains() 方法(+ 2 重载),查找此实例中是否包含指定的字符串。示例:
bool bool14 = "abc123".Contains("c1"); //true
bool bool15 = "abc123".Contains("c2"); //false
3) StartsWith() 方法(+ 2 重载),查找此实例开头是否为指定的字符串。示例:
bool bool16 = "abc123".StartsWith("ab"); //true
bool bool17 = "abc123".StartsWith("bc"); //false
bool bool18_1 = "abc".StartsWith("abc"); //true
4) EndsWith() 方法(+ 2 重载),查找此实例末尾是否为指定的字符串。示例:
bool bool18 = "abc123".EndsWith("23"); //true
bool bool19 = "abc123".EndsWith("12"); //false
bool bool20_1 = "abc".EndsWith("abc"); //true
5) Equals() 方法(+ 2 重载),比较此实例是否与指定的字符串的值相等。示例:
bool bool20 = "abc123".Equals("abc123"); //true
bool bool21 = "abc123".Equals(" abc123"); //false
bool bool21_1 = "a".Equals("A", StringComparison.CurrentCulture); //false
bool bool21_2 = "a".Equals("A", StringComparison.CurrentCultureIgnoreCase); //true
注:该方法与静态方法 Equals() 类似。
6) GetTypeCode() 方法,获取此实例对应的 System.TypeCode 枚举值。该方法实现于 System.IConvertible 接口,所有的基础数据类型都实现了该接口。示例:
TypeCode tc1 = "123".GetTypeCode(); //TypeCode.String
7) IndexOf() 方法(+ 8 重载),获取指定字符或字符串在此实例中【第一次】出现的索引,索引从零开始。示例:
int int7 = "abc123".IndexOf("b"); //1
8) IndexOfAny() 方法(+ 2 重载),获取指定的字符数组的任意字符在此实例中【第一次】出现的索引,索引从零开始。示例:
int int8 = "abc123".IndexOfAny(new char[] { '2', '3', '1' }); //3
注:可见,在查找前字符数组进行了升序排序,并没有按照索引顺序匹配。
9) LastIndexOf() 方法(+ 8 重载),获取指定字符或字符串在此实例中【最后一次】出现的索引,索引从零开始。示例:
int int9 = "abc123123".LastIndexOf("1"); //6
10) LastIndexOfAny() 方法(+ 2 重载),获取指定的字符数组的任意字符在此实例中【最后一次】出现的索引,索引从零开始。示例:
int int10 = "abc123123".LastIndexOfAny(new char[] { '2', '3', '1' }); //8
注:可见,在查找前字符数组进行了降序排序,并没有按照索引顺序匹配。
11) Insert() 方法,返回在此实例的指定索引处插入字符串后的新字符串,索引从零开始。示例:
string str16 = "abc123".Insert(0, "lcl"); //lclabc123
12) PadLeft() 方法(+ 1 重载),返回在此实例的【左侧】填充指定的字符来达到指定的长度。示例:
string str17 = "abc123".PadLeft(6, 'x'); //abc123
string str18 = "abc123".PadLeft(8, 'x'); //xxabc123
13) PadRight() 方法(+ 1 重载),返回在此实例的【右侧】填充指定的字符来达到指定的长度。示例:
string str19 = "abc123".PadRight(6, 'x'); //abc123
string str20 = "abc123".PadRight(8, 'x'); //abc123xx
14) Remove() 方法(+ 1 重载),返回在此实例中移除指定起始位置和数量后的字符串,索引从零开始。示例:
string str21 = "abc123".Remove(3, 1); //abc23
15) Replace() 方法(+ 1 重载),返回此实例在指定的旧值被新值替换后的字符串。示例:
string str22 = "abc123".Replace("abc", "def"); //def123
16) Split() 方法(+ 5 重载),返回此实例在指定的字符数组分割后的字符串数组。示例:
char[] separator = { ' ', ',' };
StringSplitOptions options = StringSplitOptions.RemoveEmptyEntries;
string[] strs1 = ",a b,c".Split(separator); //["", "a", "b", "c"]
string[] strs2 = ",a b,c".Split(separator, options); //["a", "b", "c"]
string[] strs3 = ",a b,c".Split(separator, 3); //["", "a", "b,c"]
string[] strs4 = ",a b,c".Split(separator, 3, options); //["a", "b", "c"]
string[] strs5 = "abc".Split(separator, options); //["abc"]
string[] strs6 = "".Split(separator, options); //[]
17) Substring() 方法(+ 1 重载),在此实例中检索指定起始位置至指定长度的子字符串,索引从零开始。示例:
string str23 = "abc123".Substring(1); //bc123
string str24 = "abc123".Substring(1, 2); //bc
string str24_1 = "abc123".Substring(0); //abc123
string str24_2 = "abc123".Substring(6); //""
string str24_3 = "abc123".Substring(7); //System.ArgumentOutOfRangeException:startIndex 不能大于字符串长度。
string str24_4 = "STAT items:1:number 2";
string str24_5 = str24_4.Substring(11, str24_4.LastIndexOf(':') - 11); //1 str24_4.Substring(11("1"), 12 - 11);
18) ToCharArray() 方法(+ 1 重载),将此实例复制到字符数组中,且可指定起始位置及长度,索引从零开始。示例:
char[] chars1 = "".ToCharArray(); //[]
char[] chars2 = "abc".ToCharArray(); //['a', 'b,', 'c']
char[] chars3 = "abc".ToCharArray(1, 1); //['b']
19) ToLower() 方法(+ 1 重载),返回此实例的【小写】形式的副本。示例:
string str25 = "ABc123".ToLower(); //abc123
20) ToUpper() 方法(+ 1 重载),返回此实例的【大写】形式的副本。示例:
string str26 = "abC123".ToUpper(); //ABC123
21) TrimStart() 方法(+ 1 重载),移除此实例中的【前导】空格 或 匹配指定字符数组的字符。示例:
string str27 = " okakabcok ".TrimStart(); //|okakabcok |
string str28 = " okakabcok ".TrimStart('k', ' ', 'o'); //|akabcok |
22) TrimEnd() 方法(+ 1 重载),移除此实例中的【尾部】空格 或 匹配指定字符数组的字符。示例:
string str29 = " okabcakok ".TrimEnd(); //| okabcakok|
string str30 = " okabcakok ".TrimEnd('k', ' ', 'o'); //| okabca|
23) Trim() 方法(+ 1 重载),移除此实例中的【首尾】空格 或 匹配指定字符数组的字符。示例:
string str31 = " okabcakok ".Trim(); //|okabcakok|
string str32 = " okabcakok ".Trim('k', ' ', 'o'); //|abca|
5. string 的常用扩展方法
Ø string 的扩展主要是借助了 IEnumerable<char> 接口的扩展,使用这些扩展方法之前,首先需要引入 System.Linq 命名空间。
1) 获取第一个字符
char char1 = "abc123".FirstOrDefault(); //a
2) 获取指定索引处的字符,索引从零开始
char char2 = "abc123".ElementAt(1); //b
3) 反转字符串
string str36 = new string("abc123".Reverse().ToArray()); //321cba
4) 字符串排序
string str37 = new string("132cab".OrderBy(o => o).ToArray()); //123abc
string str38 = new string("132cab".OrderByDescending(o => o).ToArray()); //cba321
5) 去除字符串中的重复字符
string str39 = new string("abc123abc123".Distinct().ToArray()); //abc123
Ø 总之,IEnumerable<char> 接口的扩展方法,字符串对象都可以运用之,因为 string 就是一个 char 类型的集合,这里就不一一列举了。
6. string 的使用技巧
1) 连接字符串
string str50 = null;
string str51 = str50 + "abc"; //abc
string str52 = string.Format("{0}abc", str50); //abc
2) 生成字符串
string str56 = string.Join(",", Enumerable.Range(1, 5)); //1,2,3,4,5
string str57 = string.Empty.PadLeft(5, 'a'); //aaaaa
3) 获取字符串宽度
string str61 = "获取字符串宽度";
using (Graphics graphics = Graphics.FromHwnd(IntPtr.Zero))
{
int fontSize = 9;
SizeF sizeF = graphics.MeasureString(str61, new Font("宋体", fontSize));
float width = sizeF.Width; //90.49804
}
说明:这里需要使用 System.Drawing.Graphics 类,需要另外引用 System.Drawing.dll 程序集。
注:WinForm 中还可以使用 Control 类,获取 Graphics 的实例,例如:
Graphics graphics = (new System.Windows.Forms.Control()).CreateGraphics();
4) 字符串与字节数组互转
//字符串 转为 字节数组
byte[] bytes1 = Encoding.Default.GetBytes("チ"); //[165, 193]
byte[] bytes2 = Encoding.Default.GetBytes("ル"); //[165, 235]
byte[] bytes3 = Encoding.Default.GetBytes("我"); //[206, 210]
byte[] bytes4 = Encoding.Default.GetBytes("a"); //[97]
byte[] bytes5 = Encoding.Default.GetBytes("A"); //[65]
byte[] bytes6 = Encoding.Default.GetBytes("1"); //[49]
byte[] bytes7 = Encoding.Default.GetBytes("#"); //[35]
byte[] bytes8 = Encoding.Default.GetBytes(""); //[]
byte[] bytes9 = Encoding.Default.GetBytes(" "); //[32]
byte[] bytes10 = Encoding.Default.GetBytes("."); //[46]
byte[] bytes11 = Encoding.Default.GetBytes("。"); //[161, 163]
byte[] bytes12 = Encoding.Default.GetBytes(","); //[44]
byte[] bytes13 = Encoding.Default.GetBytes(","); //[163, 172]
//字节数组 转为 字符串
string str66 = Encoding.Default.GetString(bytes3); //我
string str67 = Encoding.Default.GetString(bytes4); //a
5) 字符串与 Base64编码 互转
string str71 = "字符串与 Base64编码 互转";
//字符串 转为 Base64编码
byte[] byte21 = Encoding.Default.GetBytes(str71);
string str72 = Convert.ToBase64String(byte21); //19a3+7Su0+sgQmFzZTY0seDC6yC7pdeq
//Base64编码 转为 字符串
byte[] byte22 = Convert.FromBase64String(str72);
string str73 = Encoding.Default.GetString(byte22); //字符串与 Base64编码 互转
7. char 的常用静态方法
1) IsWhiteSpace() 方法(+ 2 重载),查找指定位置的字符是否为空白字符。示例:
bool bool1_1 = char.IsWhiteSpace(' '); //true
bool bool1_2 = char.IsWhiteSpace('a'); //false
bool bool1_3 = char.IsWhiteSpace("a bc", 1); //true
bool bool1_4 = char.IsWhiteSpace("a bc", 2); //false
2) IsPunctuation() 方法(+ 2 重载),查找指定位置的字符是否为标点符号。示例:
bool bool2_1 = char.IsPunctuation('a'); //false
bool bool2_2 = char.IsPunctuation(','); //true
bool bool2_3 = char.IsPunctuation("a,bc", 1); //true
bool bool2_4 = char.IsPunctuation("a,bc", 1); //true