探究 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
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...