zoukankan      html  css  js  c++  java
  • 探究 C# 中的 char 、 string(一)

    探究 C# 中的 char 、 string(一)

    1. System.Char 字符

    char 是 System.Char 的别名。

    System.Char 占两个字节,16个二进制位。

    System.Char 用来表示、存储一个 Unicode 字符。

    System.Char 的表示范围是 U+0000U+FFFF,char 默认值是 ,即 U+0000

    Unicode 的表示,通常以 U+____形式表示,即 U 和 一组16进制的数字组成。

    char 有四种赋值方法

                char a = 'j';
                char b = 'u006A';
                char c = 'x006A';
                char d = (char) 106;
                Console.WriteLine($"{a} | {b} | {c} | {d}");
    

    输出

    j | j | j | j
    

    u 开头是 Unicode 转义序列(编码);使用 Unicode 转义序列,后面必须是4个十六进制的数字。

    u006A    有效
    u06A	  无效
    u6A	  无效
    

    x 开头是 十六进制转义序列,也是由4个十六进制数字组成。如果前面是N个0的话,则可以省略0。下面的示例都是表示同一个字符。

    x006A
    x06A
    x6A
    

    char 可以隐式转为其他数值类型,整型有可以转为ushortintuintlong,和ulong,浮点型 可以转为 floatdouble,和decimal

    char 可以显式转为 sbytebyteshort

    其他类型无法隐式转为 char 类型,但是任何整型和浮点型都可以显式转为 char。

    2. 字符处理

    System.Char 中,具有很多就态方法,能够有助于识别、处理字符。

    有一个非常重要的 UnicodeCategory 枚举

      public enum UnicodeCategory
      {
        UppercaseLetter,
        LowercaseLetter,
        TitlecaseLetter,
        ModifierLetter,
        OtherLetter,
        NonSpacingMark,
        SpacingCombiningMark,
        EnclosingMark,
        DecimalDigitNumber,
        LetterNumber,
        OtherNumber,
        SpaceSeparator,
        LineSeparator,
        ParagraphSeparator,
        Control,
        Format,
        Surrogate,
        PrivateUse,
        ConnectorPunctuation,
        DashPunctuation,
        OpenPunctuation,
        ClosePunctuation,
        InitialQuotePunctuation,
        FinalQuotePunctuation,
        OtherPunctuation,
        MathSymbol,
        CurrencySymbol,
        ModifierSymbol,
        OtherSymbol,
        OtherNotAssigned,
      }
    

    System.Char 中, 有一个 GetUnicodeCategory() 静态方法,可以返回字符的类型,即上面的枚举值。

    除了 GetUnicodeCategory() ,我们还可以通过具体的静态方法判断字符的类别。

    下面列出静态方法的使用说明的枚举类别。

    静态方法 说明 枚举表示
    IsControl 值小于0x20 的不可打印字符。例如 、 、 、等。
    IsDigit 0-9和其他字母表中的数字 DecimalDigitNumber
    IsLetter A-Z、a-z 和其他字母字符 UppercaseLetter,
    LowercaseLetter,
    TitlecaseLetter,
    ModifierLetter,
    OtherLetter
    IsLetterOrDigit 字母和数字 参考 IsLetter 和 IsDigit
    IsLower 小写字母 LowercaseLetter
    IsNumber 数字、Unicode中的分数、罗马数字 DecimalDigitNumber,
    LetterNumber,
    OtherNumber
    IsPunctuation 西方和其他字母表中的标点符号 ConnectorPunctuation,
    DashPunctuation,
    InitialQuotePunctuation,
    FinalQuotePunctuation,
    OtherPunctuation
    IsSeparator 空格和所有的 Unicode 分隔符 SpaceSeparator,
    ParagraphSeparator
    IsSurrogate 0x10000到0x10FFF之间的Unicode值 Surrogate
    IsSymbol 大部分可打印字符 MathSymbol,
    ModifierSymbol,
    OtherSymbol
    IsUpper 大小字母 UppercaseLetter
    IsWhiteSpace 所有的分隔符以及 、 、 、v、f SpaceSeparator,
    ParagraphSeparator

    示例

            char chA = 'A';
            char ch1 = '1';
            string str = "test string"; 
    
            Console.WriteLine(chA.CompareTo('B'));          //-----------  Output: "-1
    														//(meaning 'A' is 1 less than 'B')
            Console.WriteLine(chA.Equals('A'));             //-----------  Output: "True"
            Console.WriteLine(Char.GetNumericValue(ch1));   //-----------  Output: "1"
            Console.WriteLine(Char.IsControl('	'));        //-----------  Output: "True"
            Console.WriteLine(Char.IsDigit(ch1));           //-----------  Output: "True"
            Console.WriteLine(Char.IsLetter(','));          //-----------  Output: "False"
            Console.WriteLine(Char.IsLower('u'));           //-----------  Output: "True"
            Console.WriteLine(Char.IsNumber(ch1));          //-----------  Output: "True"
            Console.WriteLine(Char.IsPunctuation('.'));     //-----------  Output: "True"
            Console.WriteLine(Char.IsSeparator(str, 4));    //-----------  Output: "True"
            Console.WriteLine(Char.IsSymbol('+'));          //-----------  Output: "True"
            Console.WriteLine(Char.IsWhiteSpace(str, 4));   //-----------  Output: "True"
            Console.WriteLine(Char.Parse("S"));             //-----------  Output: "S"
            Console.WriteLine(Char.ToLower('M'));           //-----------  Output: "m"
            Console.WriteLine('x'.ToString());              //-----------  Output: "x"
            Console.WriteLine(Char.IsSurrogate('U00010F00'));		// Output: "False"
            char test = 'xDFFF';
            Console.WriteLine(test);						//-----------	Output:'?'
            Console.WriteLine( Char.GetUnicodeCategory(test));//-----------	Output:"Surrogate"
    
    

    如果想满足你的好奇心,可以点击 http://www1.cs.columbia.edu/~lok/csharp/refdocs/System/types/Char.html

    3. 全球化

    C# 中 System.Char 有很丰富的方法去处理字符,例如常用的 ToUpperToLower

    但是字符的处理,会受到用户语言环境的影响。

    使用 System.Char 中的方法处理字符时,可以调用带有 Invariant 后缀的方法或使用 CultureInfo.InvariantCulture,以进行与语言环境无关的字符处理。

    示例

                Console.WriteLine(Char.ToUpper('i',CultureInfo.InvariantCulture));
                Console.WriteLine(Char.ToUpperInvariant('i'));
    

    对于字符和字符串处理,可能用到的重载参数和处理方式,请看下面的说明。

    StringComparison

    枚举 枚举值 说明
    CurrentCulture 0 使用区分文化的排序规则和当前区域性来比较字符串
    CurrentCultureIgnoreCase 1 使用对区域性敏感的排序规则,当前区域性来比较字符串,而忽略要比较的字符串的大小写
    InvariantCulture 2 使用区分文化的排序规则和不变区域性比较字符串
    InvariantCultureIgnoreCase 3 使用区分区域性的排序规则,不变区域性来比较字符串,而忽略要比较的字符串的大小写
    Ordinal 4 使用序数(二进制)排序规则比较字符串
    OrdinalIgnoreCase 5 使用序数(二进制)排序规则比较字符串,而忽略要比较的字符串的大小写

    CultureInfo

    枚举 说明
    CurrentCulture 获取表示当前线程使用的区域性的 CultureInfo对象
    CurrentUICulture 获取或设置 CultureInfo对象,该对象表示资源管理器在运行时查找区域性特定资源时所用的当前用户接口区域性
    InstalledUICulture 获取表示操作系统中安装的区域性的 CultureInfo
    InvariantCulture 获取不依赖于区域性(固定)的 CultureInfo 对象
    IsNeutralCulture 获取一个值,该值指示当前 CultureInfo 是否表示非特定区域性

    4. System.String 字符串

    4.1 字符串搜索

    字符串有多个搜索方法:StartsWith()EndsWith()Contains()IndexOf

    StartsWith()EndsWith() 可以使用 StringComparison 比较方式、CultureInfo 控制文化相关规则。

    StartsWith() :字符串开头是否存在符合区配字符串

    EndsWith(): 字符串结尾是否存在符合区配字符串

    Contains(): 字符串任意位置是否存在区配字符串

    IndexOf: 字符串或字符首次出现的索引位置,如果返回值为 -1 则表示无区配结果。

    使用示例

                string a = "痴者工良(高级程序员劝退师)";
                Console.WriteLine(a.StartsWith("高级"));
                Console.WriteLine(a.StartsWith("高级",StringComparison.CurrentCulture));
                Console.WriteLine(a.StartsWith("高级",true, CultureInfo.CurrentCulture));
                Console.WriteLine(a.StartsWith("痴者",StringComparison.CurrentCulture));
                Console.WriteLine(a.EndsWith("劝退师)",true, CultureInfo.CurrentCulture));
                Console.WriteLine(a.IndexOf("高级",StringComparison.CurrentCulture));
    

    输出

    False
    False
    False
    True
    True
    5
    

    除了 Contains(),其它三种方法都有多个重载方法,例如

    重载 说明
    (String) 是否与指定字符串区配
    (String, StringComparison) 以何种方式指定字符串区配
    (String, Boolean, CultureInfo) 控制大小写和文化规则指定字符串区配

    这些与全球化和大小写区配的规则,在后面章节中会说到。

    4.2 字符串提取、插入、删除、替换

    4.2.1 提取

    SubString() 方法可以在提取字符串指定索开始的N个长度或余下的所有的字符。

                string a = "痴者工良(高级程序员劝退师)";
                string a = "痴者工良(高级程序员劝退师)";
                Console.WriteLine(a.Substring(startIndex: 1, length: 3));
                // 者工良
                Console.WriteLine(a.Substring(startIndex: 5));
                // 高级程序员劝退师)
    

    4.2.2 插入、删除、替换

    Insert() :指定索引位置后插入字符或字符串

    Remove() :指定索引位置后插入字符或字符串

    PadLeft() :在字符串左侧将使用某个字符串扩展到N个字符长度

    PadRight():在字符串右侧将使用某个字符串扩展到N个字符长度

    TrimStart() :从字符串左侧开始删除某个字符,碰到不符合条件的字符即停止。

    TrimEnd() :从字符串右侧开始删除某个字符,碰到不符合条件的字符即停止。

    Replace():将字符串中的N连续个字符组替换为新的M个字符组。

    示例

                string a = "痴者工良(高级程序员劝退师)"; // length = 14
    
                Console.WriteLine("
      -  Remove Insert   - 
    ");
    
                Console.WriteLine(a.Insert(startIndex: 4, value: "我是"));
                Console.WriteLine(a.Remove(startIndex: 5));
                Console.WriteLine(a.Remove(startIndex: 5, count: 3));
    
                Console.WriteLine("
      -  PadLeft PadRight  -  
    ");
    
                Console.WriteLine(a.PadLeft(totalWidth: 20, paddingChar: '*'));
                Console.WriteLine(a.PadRight(totalWidth: 20, paddingChar: '#'));
                Console.WriteLine(a.PadLeft(totalWidth: 20, paddingChar: 'u0023'));
                Console.WriteLine(a.PadRight(totalWidth: 20, paddingChar: 'u002a'));
                Console.WriteLine(a.PadLeft(totalWidth: 18, paddingChar: '.'));
                Console.WriteLine(a.PadRight(totalWidth: 18, paddingChar: '.'));
    
                Console.WriteLine("
      -  Trim  -  
    ");
    
                Console.WriteLine("|Hello | World|".Trim('|'));
                Console.WriteLine("|||Hello | World|||".Trim('|'));
                Console.WriteLine("|Hello | World!|".TrimStart('|'));
                Console.WriteLine("|||Hello | World!|||".TrimStart('|'));
                Console.WriteLine("|Hello | World!|".TrimEnd('|'));
                Console.WriteLine("|||Hello | World!|||".TrimEnd('|'));
                Console.WriteLine("||||||||||||||||||||||||".TrimEnd('|'));
                
    
                Console.WriteLine("*#&abc ABC&#*".TrimStart(new char[] {'*', '#', '&'}));
                Console.WriteLine("*#&abc ABC&#*".TrimStart(new char[] {'#', '*', '&'}));
    
                Console.WriteLine("
      -  Replace  -  
    ");
    
                Console.WriteLine("abcdABCDabcdABCD".Replace(oldChar: 'a', newChar: 'A'));
    

    输出

      -  Remove Insert   -
    
    痴者工良我是(高级程序员劝退师)
    痴者工良(
    痴者工良(序员劝退师)
    
      -  PadLeft PadRight  -
    
    ******痴者工良(高级程序员劝退师)
    痴者工良(高级程序员劝退师)######
    ######痴者工良(高级程序员劝退师)
    痴者工良(高级程序员劝退师)******
    ....痴者工良(高级程序员劝退师)
    痴者工良(高级程序员劝退师)....
    
      -  Trim  -
    
    Hello | World
    Hello | World
    Hello | World!|
    Hello | World!|||
    |Hello | World!
    |||Hello | World!
    
    abc ABC&#*
    abc ABC&#*
    
      -  Replace  -
    
    AbcdABCDAbcdABCD
    

    5. 字符串驻留池

    以下为笔者个人总结,限于水平,如若有错,望各位加以批评指正。

    images

    字符串 驻留池是在域(Domain)级别完成的,而字符串驻留池可以在域中的所有程序集之间共享。

    CLR 中维护着一个叫做驻留池(Intern Pool)的表。

    这个表记录了所有在代码中使用字面量声明的字符串实例的引用。

    拼接方式操作字面量时,新的字符串又会进入字符串驻留池。

    只有使用使用字面量声明的字符串实例,实例才会对字符串驻留池字符串引用。

    而无论是字段属性或者是方法内是声明的 string 变量、甚至是方法参数的默认值,都会进入字符串驻留池。

    例如

            static string test = "一个测试";
    
            static void Main(string[] args)
            {
                string a = "a";
    
                Console.WriteLine("test:" + test.GetHashCode());
                
                TestOne(test);
                TestTwo(test);
                TestThree("一个测试");
            }
    
            public static void TestOne(string a)
            {
                Console.WriteLine("----TestOne-----");
                Console.WriteLine("a:" + a.GetHashCode());
                string b = a;
                Console.WriteLine("b:" + b.GetHashCode());
                Console.WriteLine("test - a :" + Object.ReferenceEquals(test, a));
            }
    
            public static void TestTwo(string a = "一个测试")
            {
                Console.WriteLine("----TestTwo-----");
                Console.WriteLine("a:" + a.GetHashCode());
                string b = a;
                Console.WriteLine("b:" + b.GetHashCode());
                Console.WriteLine("test - a :" + Object.ReferenceEquals(test, a));
            }
    
            public static void TestThree(string a)
            {
                Console.WriteLine("----TestThree-----");
                Console.WriteLine("a:" + a.GetHashCode());
                string b = a;
                Console.WriteLine("b:" + b.GetHashCode());
                Console.WriteLine("test - a :" + Object.ReferenceEquals(test, a));
            }
    

    输出结果

    test:-407145577
    ----TestOne-----
    a:-407145577
    b:-407145577
    test - a :True
    ----TestTwo-----
    a:-407145577
    b:-407145577
    test - a :True
    ----TestThree-----
    a:-407145577
    b:-407145577
    test - a :True
    

    可以通过静态方法 Object.ReferenceEquals(s1, s2); 或者 实例的 .GetHashCode() 来对比两个字符串是否为同一个引用。

    可以使用不安全代码,直接修改内存中的字符串

    参考 https://blog.benoitblanchon.fr/modify-intern-pool/

    string a = "Test";
    
    fixed (char* p = a)
    {
        p[1] = '3';
    }
    
    Console.WriteLine(a);
    

    使用 *Microsoft.Diagnostics.Runtime* 可以获取 CLR 的信息。

    结果笔者查阅大量资料发现,.NET 不提供 API 去查看字符串常量池里面的哈希表。

    关于 C# 字符串的使用和驻留池等原理,请参考

    http://community.bartdesmet.net/blogs/bart/archive/2006/09/27/4472.aspx

    通过设法在程序集中获取字符串文字的列表

    https://stackoverflow.com/questions/22172175/read-the-content-of-the-string-intern-pool

    .NET 底层 Profiling API说明

    https://docs.microsoft.com/en-us/dotnet/framework/unmanaged-api/profiling/profiling-overview?redirectedfrom=MSDN

    .NET字符串驻留池和提高字符串比较性能

    http://benhall.io/net-string-interning-to-improve-performance/

    关于 C# 字符串驻留池的学习文章

    https://www.cnblogs.com/mingxuantongxue/p/3782391.html

    https://www.xuebuyuan.com/189297.html

    https://www.xuebuyuan.com/189297.html

    如果总结或知识有错,麻烦大佬们斧正哈。

  • 相关阅读:
    JAVA之代理2CGLib
    Java并发编程:Lock
    synchronized详解
    redis哨兵机制--配置文件sentinel.conf详解
    redis.conf讲解
    [Java并发包学习八]深度剖析ConcurrentHashMap
    [Java并发包学习七]解密ThreadLocal
    T和Class以及Class的理解
    python学习笔记十:异常
    python学习笔记九:正则表达式
  • 原文地址:https://www.cnblogs.com/whuanle/p/11967014.html
Copyright © 2011-2022 走看看