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

来源:https://www.cnblogs.com/whuanle/archive/2019/12/01/11967014.html
-Advertisement-
Play Games

探究 C 中的 char 、 string(一) [TOC] 1. System.Char 字元 char 是 System.Char 的別名。 System.Char 占兩個位元組,16個二進位位。 System.Char 用來表示、存儲一個 Unicode 字元。 System.Char 的表示範 ...


目錄

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

1. System.Char 字元

char 是 System.Char 的別名。

System.Char 占兩個位元組,16個二進位位。

System.Char 用來表示、存儲一個 Unicode 字元。

System.Char 的表示範圍是 U+0000U+FFFF,char 預設值是 \0,即 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 的不可列印字元。例如 \r、\n、\t、\0等。
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 所有的分隔符以及 \t、\n、\r、\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('\t'));        //-----------  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("\n  -  Remove Insert   - \n");

            Console.WriteLine(a.Insert(startIndex: 4, value: "我是"));
            Console.WriteLine(a.Remove(startIndex: 5));
            Console.WriteLine(a.Remove(startIndex: 5, count: 3));

            Console.WriteLine("\n  -  PadLeft PadRight  -  \n");

            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("\n  -  Trim  -  \n");

            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("\n  -  Replace  -  \n");

            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

如果總結或知識有錯,麻煩大佬們斧正哈。


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 新聞 "擁抱可空引用類型" "介紹Orleans 3.0" 視頻及幻燈片 "組合的力量" "關於.NET:探索新的用於.NET的Azure .NET SDK" ".NET設計審查:GitHub快速審查" FableConf 2019 "OSS的樂趣" "走進編譯器與工具之旅" "使用響應式MVU釋放 ...
  • Spring Cloud Config為分散式系統提供了配置伺服器和配置客戶端,可以管理集群中的配置文件。 使用Git、SVN等版本管理系統存放配置文件,配置伺服器會到版本管理系統獲取配置,集群中的配置客戶端再到配置伺服器中獲取配置。 ...
  • [TOC] 1. 概述 本來是不想寫Paramiko的,因為我覺得之前的一篇關於Netmiko模塊更適合網工,後來發現paramiko有包含SFTP功能,所以還是有必要來講講,畢竟我們在設備上是要經常下載配置、上傳版本/升級版本用的,而且SFTP比FTP、TFTP更安全。 所以, 你也不用藉助其他工 ...
  • 01 實現自定義的可變長數組類型 假設我們要實現一個會自動擴展的數組,要實現什麼函數呢?先從下麵的main函數給出的實現,看看有什麼函數是需要我們實現的。 輸出結果: 要實現的方式,要做哪些事情呢?我先列一下: 要用動態分配的記憶體來存放數組元素,需要一個指針成員變數 重載賦值=運算符 重載[]運算符 ...
  • 01 賦值運算符重載的需求 有時候希望賦值運算符兩邊的類型可以不匹配,比如:把一個 int 類型變數賦值給一個Complex(複數)對象,或把一個 char 類型的字元串賦值給一個字元串對象,此時就需要重載賦值運算符‘=’。 需要註意的是:賦值運算符 只能重載為成員函數。 02 賦值運算符重載的例子 ...
  • 01 運算符重載的需求 C++ 預定義的運算符,只能用於基本數據類型的運算:整型、實型、字元型、邏輯型等等,且不能用於對象的運算。但是我們有時候又很需要在對象之間能用運算符,那麼這時我們就要 重載運算符 ,使得運算符能用於對象之間的運算。 比如,在數學上,兩個複數可以直接進行+、 等運算,但在C++ ...
  • 本系列將和大家分享下ASP.NET Core Web 應用程式的一些基礎知識,本章主要簡單介紹下在ASP.NET Core中如何使用AutoMapper進行實體映射。 ...
  • 作為開發人員,您始終需要處理應用程式配置數據。常見的示例是INI 文件,XML文件, .NET配置文件(也稱為“ .config”),Windows註冊表和命令行(argv)參數。配置文件的優點是它們載入速度快,不占用大量空間且易於編輯。Nini是一個功能強大的 .NET配置庫,旨在幫助快速構建高度... ...
一周排行
    -Advertisement-
    Play Games
  • Dapr Outbox 是1.12中的功能。 本文只介紹Dapr Outbox 執行流程,Dapr Outbox基本用法請閱讀官方文檔 。本文中appID=order-processor,topic=orders 本文前提知識:熟悉Dapr狀態管理、Dapr發佈訂閱和Outbox 模式。 Outbo ...
  • 引言 在前幾章我們深度講解了單元測試和集成測試的基礎知識,這一章我們來講解一下代碼覆蓋率,代碼覆蓋率是單元測試運行的度量值,覆蓋率通常以百分比表示,用於衡量代碼被測試覆蓋的程度,幫助開發人員評估測試用例的質量和代碼的健壯性。常見的覆蓋率包括語句覆蓋率(Line Coverage)、分支覆蓋率(Bra ...
  • 前言 本文介紹瞭如何使用S7.NET庫實現對西門子PLC DB塊數據的讀寫,記錄了使用電腦模擬,模擬PLC,自至完成測試的詳細流程,並重點介紹了在這個過程中的易錯點,供參考。 用到的軟體: 1.Windows環境下鏈路層網路訪問的行業標準工具(WinPcap_4_1_3.exe)下載鏈接:http ...
  • 從依賴倒置原則(Dependency Inversion Principle, DIP)到控制反轉(Inversion of Control, IoC)再到依賴註入(Dependency Injection, DI)的演進過程,我們可以理解為一種逐步抽象和解耦的設計思想。這種思想在C#等面向對象的編 ...
  • 關於Python中的私有屬性和私有方法 Python對於類的成員沒有嚴格的訪問控制限制,這與其他面相對對象語言有區別。關於私有屬性和私有方法,有如下要點: 1、通常我們約定,兩個下劃線開頭的屬性是私有的(private)。其他為公共的(public); 2、類內部可以訪問私有屬性(方法); 3、類外 ...
  • C++ 訪問說明符 訪問說明符是 C++ 中控制類成員(屬性和方法)可訪問性的關鍵字。它們用於封裝類數據並保護其免受意外修改或濫用。 三種訪問說明符: public:允許從類外部的任何地方訪問成員。 private:僅允許在類內部訪問成員。 protected:允許在類內部及其派生類中訪問成員。 示 ...
  • 寫這個隨筆說一下C++的static_cast和dynamic_cast用在子類與父類的指針轉換時的一些事宜。首先,【static_cast,dynamic_cast】【父類指針,子類指針】,兩兩一組,共有4種組合:用 static_cast 父類轉子類、用 static_cast 子類轉父類、使用 ...
  • /******************************************************************************************************** * * * 設計雙向鏈表的介面 * * * * Copyright (c) 2023-2 ...
  • 相信接觸過spring做開發的小伙伴們一定使用過@ComponentScan註解 @ComponentScan("com.wangm.lifecycle") public class AppConfig { } @ComponentScan指定basePackage,將包下的類按照一定規則註冊成Be ...
  • 操作系統 :CentOS 7.6_x64 opensips版本: 2.4.9 python版本:2.7.5 python作為腳本語言,使用起來很方便,查了下opensips的文檔,支持使用python腳本寫邏輯代碼。今天整理下CentOS7環境下opensips2.4.9的python模塊筆記及使用 ...