[Day 008 of C#] 字符串 —— heartstrings were almost broken but reconciled
Day 008
part 108 - 114.
字符串
命名空间
- namespace(命名空间) 用于解决类的重名问题,可以当做是类的文件夹,即类是属于命名空间的
- 如果当前项目中没有要用的类的命名空间,就需要手动导入:
- 用鼠标去点
- alt+shift+f10
- 手动输入
\[eg.
\]
using System;
using System.Media;
namespace _Day008_命名空间{
class Program{
static void Main(string[] args){
SoundPlayer backGroundMusic = new SoundPlayer("C:/cudo.wav");
backGroundMusic.Load();
backGroundMusic.PlayLooping();
//要使用SoundPlayer这个类,就需要引用命名空间System.Media,才能正常使用.
}
}
}
- 在上面,要使用SoundPlayer这个类来播放音乐,就需要引用命名空间System.Media,才能正常使用
在一个项目中引用另一个项目的类
- 添加引用
- 在解决方案资源管理器中,在需要引入的项目下面找到"引用",右键"添加引用",选用需要被引用的项目
- 引用命名空间 (在文件的最上面使用using关键字进行引用)
using 被引用的类中的命名空间名;
值类型和引用类型
- 值类型与引用类型的区别:
- 在内存中存储的地方不一样
- 传递的方式
-
值类型称之为值传递,引用类型称之为引用传递
-
到目前所学的值类型和引用类型:
值类型 | 引用类型 |
---|---|
int | string |
double | 自定义类 |
char | 数组 |
decimal | / |
struct | / |
enum | / |
-
两者在内存中的存储位置[1]
-
值类型的值存储在内存的栈当中
-
引用类型的值存储在内存的堆中
-
注意: 引用类型的值会存储在堆中,但同时所对应的引用类型的值的地址会被存储在栈中。
字符串
- 我们可以将字符串看做是char类型的一个只读数组
- 因此我们可以通过下标来访问字符串中的某一个元素,但不能改变这个元素
\[eg.
\]
string s = "小冰爱你"
// s[2] = '舔'; //不能这样做,因为字符串是char类型的只读数组
//如果偏要修改字符串中某一元素的值要怎么做?
//首先将字符串转换为char类型的数组
char[] c = s.ToCharArray();
c[2] = '舔'; // 但这样并不会改变字符串s的值
// 将字符数组转换为字符串
s = new string(c); //创建一个对象string,参数为char类型的数组c,会将c数组转换为字符串,并将其值赋值给字符串s,
Console.WriteLine(s[2]);
Console.ReadKey();
// 输出(没有修改前): 爱
// 输出(修改后): 舔
字符串的不可变性
- 当你给一个字符串重新赋值之后,老值并没有被销毁,而是重新开辟一块空间存储新值。例如
- 当小冰声明了一个string类型的变量str且值为"小冰",那么此时程序在内存的堆中开辟了一个空间存储了"小冰"并令其地址为0x001001,同时也在栈中开辟了一个空间,值为str的地址0x001001。
- 然后小冰又重新为str赋值为"小熊猫",但此时原来堆中的"小冰"并没有销毁,而是又在堆中新开辟了一个空间存储了"小熊猫"并令其地址为0x010101,再使栈中值为str的地址更改为0x010101,即此时str指向了"小熊猫"这一个值。
- 需要注意的是,此时"小冰"仍然被存储于内存的堆里面,但并没有任何东西指向它。这便是"字符串的不可变性",即老值并没有被覆盖,而是开辟了一块新空间存储新值。
当程序结束后,Garbage Collection会扫描整个内存,若发现有点空间没有被指向,则立即把它销毁。
- 如果两个string类型的变量的值完全相同,那么他们在堆中的地址是一样的,但在栈中的地址仍然是不一样的
如何避免垃圾内存的保留
- 通过内置的类StringBuilder
StringBuilder sb = new StringBuilder();
Stopwatch sw = new Stopwatch(); //创建了一个计时器,用来记录程序运行的时间
sw.Start(); //开始计时
string str = null;
for (int i = 0; i < 100000; i++){
sb.Append(i); //Append: 追加. 可以看做"+=". 能把i累加到sb这个对象上。
}
sw.Stop(); //结束计时
Console.WriteLine(sw.Elapsed); //Elasped属性用于获取当前对象测量出的总运行时间
// 输出: 00:00:00.0422166
- StringBuilder在内存当中并没有开空间,玩来玩去玩得都是那一块空间,但到最后我们仍然需要ToString()一下。
- 在上面,如果不用StringBuilder这个类的话,运行时间会非常非常的长,并且会占用非常大的内存,因为字符串的不可变性。
字符串的方法
- Length: 获得当前字符串中字符的个数
- ToUpper(): 将字符串的内容全部转换为大写
- ToLower(): 将字符串的内容全部转化为小写
- Equals(str, StringComparison.OridinalIgnoreCase): 比较两个字符串,能够忽略大小写
if (strA.Equals(strB, StringComparison.OridinalIgnoreCase))
- Split(): 将字符串进行分割,第一个参数的数组中的元素是所不需要的元素。并返回一个数组,里面是被分开的元素
string s = "小 ! 冰 ++ , -@ % 爱 * & ._你 "
char[] c = { ' ', '+', '-', '.', '@', '.', '_', '*', '%', '!', '&'}
//在char类型的数组c中声明一系列不需要的,需要过滤的字符
string[] res = a.Split(c, StringSplitOptions.RemoveEmptyEntries);
// Split函数中第二个枚举形参有StringSplitOptions.None或者StringSplitOptions.RemoveEmptyEntries.
//None表示返回值包括含有空字符串的数组元素
//RemoveEmptyEntries表示返回值不包括含有空字符串的数组元素
Console.ReadKey();
/*
res[0]: "小"
res[1]: "冰"
res[2]: "爱"
res[3]: "你"
- Contains(): 判断字符串中是否含有子串value(即是否含有括号里的字符串),返回bool值
- Replace(oldValue, NewValue): 将字符串中的某几个字替换成另几个字
string str = "六二、六三、六四、六五、六六...";
if (str.Contains("六四")) str = str.Replace("六四", "喵喵") //函数返回新字符串,需要接受进行替换
Console.WriteLine(str);
Console.ReadKey();
// 输出: 六二、六三、喵喵、六五、六六...
- Substring(int startIndex, int length): 截取字符串。int startIndex为元素下标,即要从哪里开始截取(包含这个位置);int length是所要截取的长度,即从截取的地方往后数length个元素
string str = "今天也要加油晚安安爱你muamuamua喵呜!"
str = str.Substring(3,16);
Console.WriteLine(str);
Console.ReadKey();
// 输出: 加油晚安安爱你muamuamua
- StartsWith():以...开始。返回bool值
string str = "今天也要加油晚安安爱你muamuamua喵呜!"
if (str.StartsWith("今天")){
Console.WriteLine(str);
}
else{
Console.WriteLine("明天也要加油晚安安爱你muamuamua喵呜!");
}
Console.ReadKey();
// 输出: 今天也要加油晚安安爱你muamuamua喵呜!
- EndsWith():以...结束。返回bool值
string str = "今天也要加油晚安安爱你muamuamua喵呜!"
if (str.StartsWith("明天")){
Console.WriteLine(str);
}
else{
Console.WriteLine("加油晚安安爱你muamuamua喵呜!");
}
Console.ReadKey();
// 输出: 加油晚安安爱你muamuamua喵呜!
- IndexOf(string value, int startIndex): 取子串value第一次出现的位置,即判断一个字符串在另一个字符串中第一次出现的位置;startIndex,搜索起始位置。都返回int类型。找不到则返回-1
string str = "今天也要加油晚安安爱你muamuamua喵呜!"
int index = str.IndexOf("安", 8);
Console.WriteLine(index);
Console.ReadKey();
// 输出: 8
- LastIndexOf(): 判断一个字符串在另一个字符串中最后一次出现的位置;
string str = "今天也要加油晚安安爱你muamuamua喵呜!"
int index = str.LastIndexOf("安");
Console.WriteLine(index);
Console.ReadKey();
// 输出: 8
- Trim(): 去除前后的空格
- TrimEnd(): 去掉前面的空格
- TrimStart(): 去掉后面的空格
- IsNullOrEmpty(): 判断是null还是空(两者都可以)
- string.Join(string separator, params object[] values):
- string separator: 要用作分隔符的字符串
- params: 可变参数
string [] names = { "小冰", "保密", "小熊猫", "必应", "草莓" }
string str = string.Join("|", names);
Console.WriteLine(str);
Console.ReadKey();
// 输出: 小冰|保密|小熊猫|必应|草莓
练习
- 课上练习4:文本文件中存储了多个文章标题、作者,标题和作者之间用若干空格(数量不定)隔开,每行一个。
- 标题有的长有的短,输出到控制台的时候最多标题长度10。
- 如果超过10,则截取长度8的子串并且最后添加“...”,加一个竖线后输出作者的名字。
路径: G:\Movies\Friend\QWQ.txt
博客园主题SimpleMemory如何在开关Darkmode的同时切换代码高亮的主题 小冰
CSharp基础之循环结构while/do-while 老赵
十七岁初识禁果 金井纮
坏蛋是怎样炼成的 六道
using System.IO;
...
...
//如何读取一个txt文件的内容
string path = @"G:\Movies\Friend\QWQ.txt";
string[] contents = File.ReadAllLines(path, Encoding.Default);
//读取所有行. Encoding.Default解决中文乱码问题
for (int i = 0; i < contents.Length; i++){
string[] str = contents[i].Split(new char[] { ' ' }, StringSplitOptions.RemoveEmptyEntries)
Console.WriteLine((str[0].Length > 10 ? str[0].Substring(0,8)+"......":str[0])+"|"+str[1]);
}
// 三元表达式: 如果str[0]的长度大于10,则取str[0]的前八位字符,否则就str[0]
Console.ReadKey();
sentences
小冰咕咕咕
:3
存储位置: 内存被人为分成5块,但在内存里有3块地方我们经常使用:堆、栈、静态存储区域 ↩︎