[Day 008 of C#] 字符串 —— heartstrings were almost broken but reconciled

Day 008

part 108 - 114.

字符串


命名空间

  • namespace(命名空间) 用于解决类的重名问题,可以当做是类的文件夹,即类是属于命名空间的
  • 如果当前项目中没有要用的类的命名空间,就需要手动导入:
  1. 用鼠标去点
  2. alt+shift+f10
  3. 手动输入

\[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,才能正常使用

在一个项目中引用另一个项目的类

  1. 添加引用
  • 在解决方案资源管理器中,在需要引入的项目下面找到"引用",右键"添加引用",选用需要被引用的项目
  1. 引用命名空间 (在文件的最上面使用using关键字进行引用)
using 被引用的类中的命名空间名;

值类型和引用类型

  • 值类型与引用类型的区别:
  1. 在内存中存储的地方不一样
  2. 传递的方式
  • 值类型称之为值传递,引用类型称之为引用传递

  • 到目前所学的值类型和引用类型:

值类型 引用类型
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这个类的话,运行时间会非常非常的长,并且会占用非常大的内存,因为字符串的不可变性。

字符串的方法

  1. Length: 获得当前字符串中字符的个数
  2. ToUpper(): 将字符串的内容全部转换为大写
  3. ToLower(): 将字符串的内容全部转化为小写
  4. Equals(str, StringComparison.OridinalIgnoreCase): 比较两个字符串,能够忽略大小写
if (strA.Equals(strB, StringComparison.OridinalIgnoreCase))
  1. 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]: "你"
  1. Contains(): 判断字符串中是否含有子串value(即是否含有括号里的字符串),返回bool值
  2. Replace(oldValue, NewValue): 将字符串中的某几个字替换成另几个字
string str = "六二、六三、六四、六五、六六...";
if (str.Contains("六四")) str = str.Replace("六四", "喵喵") //函数返回新字符串,需要接受进行替换
Console.WriteLine(str);
Console.ReadKey();
// 输出: 六二、六三、喵喵、六五、六六...
  1. Substring(int startIndex, int length): 截取字符串。int startIndex为元素下标,即要从哪里开始截取(包含这个位置);int length是所要截取的长度,即从截取的地方往后数length个元素
string str = "今天也要加油晚安安爱你muamuamua喵呜!"
str = str.Substring(3,16);
Console.WriteLine(str);
Console.ReadKey();
// 输出: 加油晚安安爱你muamuamua
  1. StartsWith():以...开始。返回bool值
string str = "今天也要加油晚安安爱你muamuamua喵呜!"
if (str.StartsWith("今天")){
	Console.WriteLine(str);
}
else{
	Console.WriteLine("明天也要加油晚安安爱你muamuamua喵呜!");
}
Console.ReadKey();
// 输出: 今天也要加油晚安安爱你muamuamua喵呜!
  1. EndsWith():以...结束。返回bool值
string str = "今天也要加油晚安安爱你muamuamua喵呜!"
if (str.StartsWith("明天")){
	Console.WriteLine(str);
}
else{
	Console.WriteLine("加油晚安安爱你muamuamua喵呜!");
}
Console.ReadKey();
// 输出: 加油晚安安爱你muamuamua喵呜!
  1. IndexOf(string value, int startIndex): 取子串value第一次出现的位置,即判断一个字符串在另一个字符串中第一次出现的位置;startIndex,搜索起始位置。都返回int类型。找不到则返回-1
string str = "今天也要加油晚安安爱你muamuamua喵呜!"
int index = str.IndexOf("安", 8);
Console.WriteLine(index);
Console.ReadKey();
// 输出: 8
  1. LastIndexOf(): 判断一个字符串在另一个字符串中最后一次出现的位置;
string str = "今天也要加油晚安安爱你muamuamua喵呜!"
int index = str.LastIndexOf("安");
Console.WriteLine(index);
Console.ReadKey();
// 输出: 8
  1. Trim(): 去除前后的空格
  2. TrimEnd(): 去掉前面的空格
  3. TrimStart(): 去掉后面的空格
  4. IsNullOrEmpty(): 判断是null还是空(两者都可以)
  5. 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


  1. 存储位置: 内存被人为分成5块,但在内存里有3块地方我们经常使用:堆、栈、静态存储区域 ↩︎

posted @ 2022-03-30 20:18  冰导有盒饭  阅读(41)  评论(0)    收藏  举报