C#,入门教程(43)——字节、字符、字符串及操作函数之基础知识总结与实例源代码(2023清明之作)

        字符串是C#等较高级编程语言区别于中级编程C语言的重要内容,充分而必要的字符串基础类class、函数method与模块module大大提高了软件开发的效率,并且因为避免指针pointer的操作,软件的可靠性与安全性得以提高。

        作者常年从事商业化、工业化软件的开发,希望通过这篇文章将字符、字符串相关的部分基础知识传达给初学者。本文既有知识讲解,也有应用编程实例。

1 字符相关的数据类型

1.1 字节 byte

        byte 型数据是几乎所有数据类型的基础。

        byte类型的数值范围是0~255。

        转换为二进制是00000000~11111111。

1.2 致命的字符集问题

        字符集是字符、字符串相关的核心问题。

        几乎所有参与国际化软件的开发者都会因为字符集的问题犯错误或陷入困境。

         图中可以看到,同样的字符串数据,在不同的字符集转换后,显示结果往往完全不同。

        字符集的问题展开就是一篇长文,这里不展开了。

        只需记住:出现乱码一般都是字符集问题!

1.3 字符 char

        C# 中的字符与C语言中字符差别很大。

        C# 中的 char 是一个类,是 System.Char 的别名。其内部类似于:

namespace System
{
    public class Char
    {
        private byte[] Buffer;
        public Char()
        {
        }
    }
}

         可见,char 内部数据实际上是一个 byte 数组。

        微软:.NET 使用 Char 结构通过 UTF-16 编码来表示 Unicode 码位。 对象的值 Char 是其 16 位数值 (序) 值。后面会讲解如何转换。

        下面的代码可以初步了解 char 类的一些函数。

using System.Text;

private void button1_Click(object sender, EventArgs e)
{
    char chA = 'A';
    char ch1 = '1';
    string str = "test string";
 
    sb.AppendLine(chA.CompareTo('B'));          //-----------  Output: "-1" (meaning 'A' is 1 less than 'B')
    sb.AppendLine(chA.Equals('A'));             //-----------  Output: "True"
    sb.AppendLine(Char.GetNumericValue(ch1));   //-----------  Output: "1"
    sb.AppendLine(Char.IsControl('\t'));        //-----------  Output: "True"
    sb.AppendLine(Char.IsDigit(ch1));           //-----------  Output: "True"
    sb.AppendLine(Char.IsLetter(','));          //-----------  Output: "False"
    sb.AppendLine(Char.IsLower('u'));           //-----------  Output: "True"
    sb.AppendLine(Char.IsNumber(ch1));          //-----------  Output: "True"
    sb.AppendLine(Char.IsPunctuation('.'));     //-----------  Output: "True"
    sb.AppendLine(Char.IsSeparator(str, 4));    //-----------  Output: "True"
    sb.AppendLine(Char.IsSymbol('+'));          //-----------  Output: "True"
    sb.AppendLine(Char.IsWhiteSpace(str, 4));   //-----------  Output: "True"
    sb.AppendLine(Char.Parse("S"));             //-----------  Output: "S"
    sb.AppendLine(Char.ToLower('M'));           //-----------  Output: "m"
    sb.AppendLine('x'.ToString());              //-----------  Output: "x"
    MessageBox.Show(sb.ToString());
}

         char 是一个“字”。一般情况下,一个字母,一个汉字,一个符号都是一个字符。

        更多细节,请阅读微软官方的说明(现在不读,以后再读):

System.Charhttps://learn.microsoft.com/zh-cn/dotnet/api/system.char?view=net-7.0

1.4 字符串 string

        字符串数据类型 string ,也是一个类,是 System.String 的别名。其内部类似于:

namespace System
{
    public class String
    {
        private byte[] Buffer;
        public String()
        {
        }
    }
}

        可见 string 与 char 有点类似。不过其功能要强大很多。

        更多细节请阅读微软官方文章(现在不读,以后再读):

System.Stringhttps://learn.microsoft.com/zh-cn/dotnet/api/system.string?view=net-7.0

2 字符数据类型之间的转换

2.1 byte[] -> char[]

        byte数组转为char数组

byte[] bb = new byte[5] { 0x31, 0x32, 0x33, 0x34, 0x35 };
char[] cc = Encoding.ASCII.GetChars(bb);

2.2 char[] -> byte[]

char数组转为byte数组

char[] cc = new char[5] { 'a', 'b', 'c', 'd', 'e' };
byte[] bb = Encoding.Default.GetBytes(cc);

2.3 string -> char[]

        字符串转为char数组

string ss = "abcd";
char[] cc = ss.ToCharArray();

2.4 char[] -> string

        char数组转为string

char[] cc = { 'a', 'b', 'c', 'd' };
string ss = new string(cc);

 2.5 string -> byte[]

        字符串转为byte数组

string ss = "abcd";
byte[] bb = Encoding.UTF8.GetBytes(ss);

2.6 byte[] -> string

        byte数组转为字符串

byte[] bb = { 0x31, 0x32, 0x33, 0x34 };
string ss = Encoding.UTF8.GetString(bb);

3 其他数据转为字符串

        除了 byte,char 之外的其他数据转为 string ,主要有以下方法:

(1)ToString() 方法。

int a = 10;
string b = a.ToString();

(2)加号,默认调用ToString()

int a = 10;
string b = a + ".00";

(3)String.Format 方法

        请见下面的 4.8 Format 

(4)更复杂的数据类型

        类class等更复杂的数据类型,推荐使用 Newtonsoft.JsonConvertor 转为 json 或者其他 xml convertor 转为 xml 数据。

4 字符串的操作

        初学提示:以下函数,如果前面有 static 关键字(属于静态方法),使用 String. 开始。否则是属于字符串 实例 的方法。

4.1 字符串克隆 Clone

        public static string Clone();

        返回对此 String 实例的引用。

string strB = (string)strA.Clone();

        字符串的 克隆 与 赋值、复制 有什么区别?请继续阅读。

4.2 字符串的比较

        String 字符串类有用于两个字符串比较的常用方法(9个重载)。

4.2.1 public static int Compare( string strA, string strB )

        比较两个指定的 string 对象,并返回一个表示它们在排列顺序中相对位置的整数。该方法区分大小写。等同于 Compare( string strA, string strB, false)。

string strA = "abc";
string strB = "aBb";
int result = String.Compare(strA, strB);

4.2.2  public static int Compare( string strA, string strB, bool ignoreCase )

        比较两个指定的 string 对象,并返回一个表示它们在排列顺序中相对位置的整数。但是,如果布尔参数为真时,该方法不区分大小写。

// 大小写敏感的比较
int result_f = String.Compare(strA, strB, false);
// 大小写不敏感的比较
int result_t = String.Compare(strA, strB, true);

        推荐语法(阅读起来更直观,比 false true 不容易产生异议):

// 忽略大小写(大小写不敏感)
int result = String.Compare(strB, strB, StringComparison.OrdinalIgnoreCase);
// or
int result = String.Compare(strB, strB, StringComparison.Ordinal);

4.2.3 比较的结果

        返回一个 32 位带符号整数(int),该整数代表 strA 与 strB 之间的位置关系:

  • 小于零: strA 小于 strB,strA在strB之前;
  • 等于零:strA 等于 strB,位置相同;
  • 大于零:strA 大于 strB,strB在 strA 之前。

4.2.4 按字符进行的精确比较(CompareOrdinal)

        public static int CompareOrdinal(string strA, string strB)

        通过计算每个字符串中相应 Char 对象的数值来比较两个指定的 String 对象。

        public static int CompareOrdinal(string strA, int indexA, string strB, int indexB, int length)

        通过计算两个指定的 String 对象的每个子字符串中相应 Char 对象的数值比较子字符串。

  • strA 要在比较中使用的第一个字符串。
  • indexA strA 中子字符串的起始索引。
  • strB 要在比较中使用的第二个字符串。
  • indexB strB 中子字符串的起始索引。
  • length 要比较的子字符串中字符的最大数量。
  • 返回值 一个 32 位带符号整数,指示两个比较数之间的词法关系。 值 Condition 小于零 strA 中的子字符串小于 strB 中的子字符串。 零 子字符串相等,或者 length 为零。 大于零 strA 中的子字符串大于 strB 中的子字符串。
private void button1_Click(object sender, EventArgs e)
{
    string strLow = "abc";
    string strCap = "ABC";
    string result = "equal to ";
    int pos = 1;

    // The Unicode codepoint for 'b' is greater than the codepoint for 'B'.
    int x = String.CompareOrdinal(strLow, pos, strCap, pos, 1);
    if (x < 0) result = "less than";
    if (x > 0) result = "greater than";

    StringBuilder sb = new StringBuilder();
    sb.AppendLine("CompareOrdinal(\"{0}\"[{2}], \"{1}\"[{2}]):", strLow, strCap, pos);
    sb.AppendLine("   '{0}' is {1} '{2}'", strLow[pos], result, strCap[pos]);

    // In U.S. English culture, 'b' is linguistically less than 'B'.
    x = String.Compare(strLow, pos, strCap, pos, 1, false, new CultureInfo("en-US"));
    if (x < 0) result = "less than";
    else if (x > 0) result = "greater than";
    sb.AppendLine("Compare(\"{0}\"[{2}], \"{1}\"[{2}]):", strLow, strCap, pos);
    sb.AppendLine("   '{0}' is {1} '{2}'", strLow[pos], result, strCap[pos]);
    MessageBox.Show(sb.ToString(), "字符串比较CompareOrdinal");
}

4.3 字符串的连接(加号重载)

        将两个(或更多)字符串连接起来,即可以使用Concat函数,也可以直接使用 String 类重载的加号。

        连接两个 string 对象。

        public static string Concat( string str0, string str1 )

        连接三个 string 对象。
        public static string Concat( string str0, string str1, string str2 )

        连接四个 string 对象。
        public static string Concat( string str0, string str1, string str2, string str3 )

        
        。。。

string strC = strA + strB;
string strC = strA + "-" + strB;
string strC = String.Concat(strA, strB);
string strD = String.Concat(strA, strB, strC);

        推荐语法:用 + (加号)简单直观。


4.4 字符串含有子串?

        Contains 函数用于判断是否含有子串。

        public bool Contains( string value )
        返回一个表示指定 string 对象是否出现在字符串中的值。

string s1 = "The quick brown fox jumps over the lazy dog";
string s2 = "fox";
bool b = s1.Contains(s2);

        Contains 函数与 IndexOf 函数功能类似, IndexOf 可以返回定位位置信息,更实用。

        推荐语法:用 IndexOf

4.5 字符串的复制

4.5.1 字符串复制

        public static string Copy( string str )
        创建一个与指定字符串具有相同值的新的 String 对象。

string s2 = String.Copy(s1);

        数据的复制基本形式是:T a = b;

        字符串也如此:string s2 = s1;

        这个与 Copy 有什么区别呢?没有。

        推荐语法:= 等号赋值!

4.5.2 字符串的 克隆 与 赋值、复制的区别?

string s1 = (string)strA.Clone();
string s2 = String.Copy(strA);
string s3 = strA;
strA = "xYz";
MessageBox.Show("strA=" + strA + "\ns1=" + s1 + "\ns2=" + s2 + "\ns3=" + s3);

        运行结果:

4.6 部分复制(字符数组的局部提取)CopyTo

        public void CopyTo( int sourceIndex, char[] destination, int destinationIndex, int count )
        从 string 对象的指定位置开始复制指定数量的字符到 Unicode 字符数组中的指定位置。

  • sourceIndex 要复制的此实例中第一个字符的索引。
  • destination 此实例中的字符所复制到的 Unicode 字符数组。
  • destinationIndex destination 中的索引,在此处开始复制操作。
  • count 此实例中要复制到 destination 的字符数。
string strSource = "changed";
char[] destination = new char[strSource.Length];
strSource.CopyTo(0, destination, 4, strSource.Length);


4.7 尾部匹配 EndsWith

        public bool EndsWith(string value)

        确定此字符串实例的结尾是否与指定的字符串匹配。

        此方法将 value 与位于此实例末尾、与 value 长度相同的子字符串进行比较,并返回它们是否相等的指示。 若要相等,value 必须是对此同一实例的引用,或者与此实例的末尾匹配。

        此方法使用当前区域性执行单词(区分大小写和区域性)比较。

if(strA.EndsWith("d")) 
{
    MessageBox.Show(strA + " Endswith d");
}
else 
{
    MessageBox.Show(strA + " NOT Endswith d");
}

4.8 数据格式化为字符串的通用函数 Format

        数据格式化是一个比较大的课题。

        可以阅读:

微软官方文章https://learn.microsoft.com/zh-cn/previous-versions/dotnet/netframework-4.0/26etazsy(v=vs.100)?redirectedfrom=MSDN

4.8.1 用格式化字符串

        这里简单举几个例子:

private void button1_Click(object sender, EventArgs e)
{
    string d1 = String.Format("{0:D}abc{1:D}", 11, 12);
    string d2 = String.Format("{0:D8}", 13);
    string f1 = String.Format("{0:F}abc{1:F}", 21.0, 22.0);
    string f2 = String.Format("{0:F8}", 23.0);
    MessageBox.Show("f1=" + d1 + "\nd2=" + d2 + "\nf1=" + f1 + "\nf2=" + f2);
}

        知识要点:

        (1){0:D} 是所谓的格式化字符串;冒号前的数字对应后面输出数据的序号,从0开始。

        (2)D F 代表对应输出为整数、浮点数;

        (3)D F 后面的数字代表位数;位数不够的自动添加0;

4.8.2 插入字串(Insert String)变量插入格式

        新的C#语法可以使用“Insert String”即所谓的插入字符串直接输出数据。

        简单的例子是:

private void button1_Click(object sender, EventArgs e)
{
    int a1 = 11;
    int a2 = 12;
    int a3 = 23;
    string d3 = $"{a1} + {a2} = {a3}";
    MessageBox.Show(d3);
}

        运行结果当然如你所愿 :-)

        知识要点:

        (1)字符串外用  $ 开始;

        (2)字符串内直接用 { } 把变量括起来即可;

        (3)更多有关格式化的输出,请阅读其他文章。

        推荐语法:(1)调试输出,用 insert string;(2)正式输出用 Format。

4.9 定位子串

        public int IndexOf(string subStr)

        找到子串,返回第一个子串的起始位置(0起步);

        找不到子串,返回 -1。

private void button1_Click(object sender, EventArgs e)
{
    string s1 = "abcdefghhijkl";
    string s2 = "jk";
    int pos = s1.IndexOf(s2);
    if (pos >= 0)
    {
        MessageBox.Show("字符串" + s1 + "中第" + pos + "索引位置找到子串" + s2, "String.IndexOf");
    }
    else
    {
        MessageBox.Show("无法找到子串");
    }
}

        IndexOf 与 Contains 的区别在于 Contains 无法获得子串的起始位置。

4.10 多字符搜索 IndexOfAny

        public int IndexOfAny(char[] angof)

        搜索指定 Unicode 字符数组中的任意字符在此实例中第一个匹配项的索引。

private void button1_Click(object sender, EventArgs e)
{
    char[] pc = { '.', ',', ';' };
    string ss = "Rover, jump the river!";
    int pos = ss.IndexOfAny(pc);
    if (pos >= 0)
    {
        MessageBox.Show("字符串" + ss + "中第" + pos + "索引位置找到字符" + new string(pc) + "之一", "String.IndexOf");
    }
    else
    {
        MessageBox.Show("无法找到任意一个字符");
    }
}

4.11 插入子串 Insert

        public string Insert(int startIndex, string value)
        在此实例中的指定索引位置插入一个指定的 String 实例。

  • startIndex 此插入的索引位置。
  • value 要插入的字符串。
private void button11_Click(object sender, EventArgs e)
{
    string animal1 = "fox jump over eagle";
    string animal2 = "dog";

    animal1 = animal1.Insert(12, animal2);
    MessageBox.Show(animal1);
}

4.12 字符串是否符合 Unicode IsNormalized

        public bool IsNormalized()
        判别此字符串是否符合 Unicode 范式 C。
        返回值,如果此字符串符合范式 C,则为 true;否则为 false。

4.13 空串判别 IsNullOrEmpty

        public static bool IsNullOrEmpty(string value)

        判别指定的字符串是 null 还是 Empty 字符串。

  • value 要测试的字符串。
  • 返回值 如果 value 参数为 null 或空字符串 (""),则为 true;否则为 false。
if (String.IsNullOrEmpty(strA))
{
}

        等同于:

if(strA == null || strA == "")
{
}

4.14 定位最后一个子串

        public int LastIndexOf(string subStr)

        找到子串,返回最后一个子串的起始位置(0起步);

        找不到子串,返回 -1。

private void button1_Click(object sender, EventArgs e)
{
    string s1 = "abcjkdefghhijkl";
    string s2 = "jk";
    int pos = s1.LastIndexOf(s2);
    if (pos >= 0)
    {
        MessageBox.Show("字符串" + s1 + "中第" + pos + "索引位置找到子串" + s2, "String.IndexOf");
    }
    else
    {
        MessageBox.Show("无法找到子串");
    }
}


4.15 数组组合成字符串的方法 Join

        public static string Join(string separator, params Object[] values)

        Join函数用“分隔符separator”将“数组values”组合成一个长的字符串。 

        串联对象数组的各个元素,其中在每个元素之间使用指定的分隔符。

        下面的实例先计算小于100的质数,存入数组。最后用 Join 方法组成字符串输出。


private static int[] GetPrimes(int maxPrime)
{
    Array values = Array.CreateInstance(typeof(int), new int[] { maxPrime - 1 }, new int[] { 2 });
    // Use Sieve of Erathsthenes to determine prime numbers.
    for (int ctr = values.GetLowerBound(0); ctr <= (int)Math.Ceiling(Math.Sqrt(values.GetUpperBound(0))); ctr++)
    {

        if ((int)values.GetValue(ctr) == 1) continue;

        for (int multiplier = ctr; multiplier <= maxPrime / 2; multiplier++)
        {
            if (ctr * multiplier <= maxPrime)
            {
                values.SetValue(1, ctr * multiplier);
            }
        }
    }

    List<int> primes = new List<int>();
    for (int ctr = values.GetLowerBound(0); ctr <= values.GetUpperBound(0); ctr++)
    {
        if ((int)values.GetValue(ctr) == 0)
        {
            primes.Add(ctr);
        }
    }
    return primes.ToArray();
}

private void button1_Click(object sender, EventArgs e)
{
    int maxPrime = 100;
    int[] primes = GetPrimes(maxPrime);
    string ps = String.Join(",", primes);
    MessageBox.Show($"Primes less than {maxPrime}: {ps}");
}

4.16 左侧填满 PadLeft

        public string PadLeft(int totalWidth)
        通过在此实例中的字符左侧填充空格来达到指定的总长度,从而实现右对齐。

        totalWidth 结果字符串中的字符数,等于原始字符数加上任何其他填充字符。

4.17 右侧填满 PadRight

        public string PadRight(int totalWidth)
        通过在此实例中的字符右侧填充空格来达到指定的总长度,从而实现左对齐。

        totalWidth 结果字符串中的字符数,等于原始字符数加上任何其他填充字符。

        上述两个函数用于字符串对齐,很少使用。

4.18 删除子串 Remove

        public string Remove(int startIndex,int count)
        从此实例中的指定位置开始删除指定数目的字符。

  • startIndex 开始删除字符的从零开始的位置。
  • count  要删除的字符数。
     

4.19 子串替换 Replace

        public string Replace(string oldValue,string newValue)
        返回一个新字符串,其中当前实例中出现的所有指定字符串替换为另一个指定的字符串。

  • oldValue 要被替换的字符串。
  • newValue 要替换出现的所有 oldValue 的字符串。

        子串替换是经常使用的字符串函数,下面的字符串将一段 HTML 网页内容替换为桌面系统显示的常规字符串格式。

private void button1_Click(object sender, EventArgs e)
{
    string html = "Welcome!<br>Hello World!<br>";
    string msg = html.Replace("<br>","\n");
    MessageBox.Show(msg);
}

        注意:上面的文字中红色“都”字,代表每个子串都替换!

4.20 字符串分解 Split

        public string[] Split(char[] separator,StringSplitOptions options)
        返回的字符串数组包含此字符串中的子字符串(由指定 Unicode 字符数组的元素分隔)。 参数指定是否返回空数组元素。

  • separator 分隔此字符串中子字符串的 Unicode 字符数组、不包含分隔符的空数组或 null。
  • options 要省略返回的数组中的空数组元素,则为 RemoveEmptyEntries;要包含返回的数组中的空数组元素,则为 None。

        Split 是最常用的字符串函数之一。

private void button11_Click(object sender, EventArgs e)
{
    string s1 = ",one,,,two,,,,,three";
    string[] s2 = s1.Split(new char[] { ',' }, StringSplitOptions.None);
    string[] s3 = s1.Split(new char[] { ',' }, StringSplitOptions.RemoveEmptyEntries);
    MessageBox.Show("s2 length=" + s2.Length + "\ns3 length=" + s3.Length);
}

         推荐语法:一定要用 StringSplitOptions !


4.21 起始位置匹配 StartsWith

        public bool StartsWith(string value)
        确定此字符串实例的开头是否与指定的字符串匹配。

        value 要比较的字符串。

private void button1_Click(object sender, EventArgs e)
{
    string s1 = "Hello world!";
    bool ok = s1.StartsWith("h");
    MessageBox.Show("ok=" + ((ok) ? "yes" : "no"));
}

4.22 提取子串 Substring

        public string Substring(int startIndex,int length)
        从此实例检索子字符串。 子字符串从指定的字符位置开始且具有指定的长度。

  • startIndex 此实例中子字符串的起始字符位置(从零开始)。
  • length 子字符串中的字符数。
private void button1_Click(object sender, EventArgs e)
{
    string s1 = "Hello world!";
    string s2 = s1.Substring(1, 3);
    MessageBox.Show("s2=" + s2);
}


4.23 转为小写 ToLower

private void button1_Click(object sender, EventArgs e)
{
    string s1 = "Hello world!";
    string s2 = s1.Substring(0, 3);
    MessageBox.Show("s2=" + s2.ToLower());
}


4.24 转为大写 ToUpper

private void button1_Click(object sender, EventArgs e)
{
    string s1 = "Hello world!";
    string s2 = s1.Substring(1, 3);
    MessageBox.Show("s2=" + s2.ToUpper());
}


4.25 删除空白字符 Trim

        public string Trim()
        从当前 String 对象移除所有前导空白字符和尾部空白字符。
        空白字符由 Unicode 标准定义。
        Trim() 方法删除在传递到 Char.IsWhiteSpace 方法时生成返回值 true 的任何前导字符和尾随字符。

        另外 TrimEnd TrimStart 功能类似。

private void button1_Click(object sender, EventArgs e)
{
    string s1 = " \t Hello world!\r\n}";
    MessageBox.Show("s1 trim =" + s1.Trim());
}

        推荐语法:Trim() 应该与任何字符串处理“如影随形”。

        短时间写出,不当之处请指正。

-----------------------------------------------------------------------------------------

        仅以此文祭奠年初仙逝的父亲。

        今天是清明节,您不喜欢烧纸与燃香这些形式的东西,
儿子就写篇技术文章纪念一下。

       写得不好,请您海涵。

-----------------------------------------------------------------------------------------

猜你喜欢

转载自blog.csdn.net/beijinghorn/article/details/129966822