Структура System.Char
В этой статье приводятся дополнительные замечания к справочной документации по этому API.
Структура Char представляет кодовые точки Юникода с помощью кодировки UTF-16. Значением объекта Char является его 16-разрядное числовое (порядковое) значение.
Если вы не знакомы с Юникодом, скалярными значениями, точками кода, суррогатными парами, UTF-16 и типом Rune , см . статью "Введение в кодировку символов" в .NET.
В этой статье рассматриваются отношения между Char объектом и символом и обсуждаются некоторые распространенные задачи, выполняемые с Char экземплярами. Рекомендуется рассмотреть тип, представленный Rune в .NET Core 3.0, в качестве альтернативы выполнению Char некоторых из этих задач.
Объекты типа char, символы Юникода и строки
Объект String является упорядоченной коллекцией структур Char, представляющей строку текста. Большинство символов Юникода могут быть представлены одним объектом Char, но знак, который кодируется как базовый символ, суррогатная пара и/или последовательность несамостоятельных знаков представляется с помощью нескольких объектов Char. Поэтому структура Char в объекте String не обязательно эквивалентна одному символу Юникода.
Для представления одного символа Юникода используется несколько 16-разрядных кодовых единиц в следующих случаях:
Глифы, которые могут состоять из одного символа или базового символа со следующими за ним несамостоятельными знаками (одним или более). Например, символ ä представлен объектом Char с кодовой единицей U+0061, за которым следует объект Char с кодовой единицей U+0308. (Символ ä также может быть представлен как один объект Char с кодовой единицей U+00E4.) В следующем примере показано, что символ ä состоит из двух объектов Char.
using System; using System.IO; public class Example1 { public static void Main() { StreamWriter sw = new StreamWriter("chars1.txt"); char[] chars = { '\u0061', '\u0308' }; string strng = new String(chars); sw.WriteLine(strng); sw.Close(); } } // The example produces the following output: // ä
open System open System.IO let sw = new StreamWriter("chars1.txt") let chars = [| '\u0061'; '\u0308' |] let string = String chars sw.WriteLine string sw.Close() // The example produces the following output: // ä
Imports System.IO Module Example2 Public Sub Main() Dim sw As New StreamWriter("chars1.txt") Dim chars() As Char = {ChrW(&H61), ChrW(&H308)} Dim strng As New String(chars) sw.WriteLine(strng) sw.Close() End Sub End Module ' The example produces the following output: ' ä
Символы вне основной многоязыковой плоскости Юникода (BMP). Юникод поддерживает шестнадцать плоскостей помимо BMP, которая представляет плоскость 0. Кодовая точка Юникода в UTF-32 представляется 21-битовым значением, которое содержит плоскость. Например, U+1D160 представляет символ MUSICAL SYMBOL EIGHTH NOTE (музыкальный символ восьмая). Поскольку кодировка UTF-16 имеет только 16 разрядов, символы за пределами BMP в кодировке UTF-16 представляются суррогатными парами. В следующем примере показано, что U+D834 U+DD60 является эквивалентом U+1D160, символа MUSICAL SYMBOL EIGHTH NOTE, в UTF-32. U+D834 является старшим символом-заместителем; старшие символы-заместители находятся в диапазоне от U+D800 до U+DBFF. U+DD60 является младшим символом-заместителем; младшие символы-заместители находятся в диапазоне от U+DC00 до U+DFFF.
using System; using System.IO; public class Example3 { public static void Main() { StreamWriter sw = new StreamWriter(@".\chars2.txt"); int utf32 = 0x1D160; string surrogate = Char.ConvertFromUtf32(utf32); sw.WriteLine("U+{0:X6} UTF-32 = {1} ({2}) UTF-16", utf32, surrogate, ShowCodePoints(surrogate)); sw.Close(); } private static string ShowCodePoints(string value) { string retval = null; foreach (var ch in value) retval += String.Format("U+{0:X4} ", Convert.ToUInt16(ch)); return retval.Trim(); } } // The example produces the following output: // U+01D160 UTF-32 = ð (U+D834 U+DD60) UTF-16
open System open System.IO let showCodePoints (value: char seq) = let str = value |> Seq.map (fun ch -> $"U+{Convert.ToUInt16 ch:X4}") |> String.concat "" str.Trim() let sw = new StreamWriter(@".\chars2.txt") let utf32 = 0x1D160 let surrogate = Char.ConvertFromUtf32 utf32 sw.WriteLine $"U+{utf32:X6} UTF-32 = {surrogate} ({showCodePoints surrogate}) UTF-16" sw.Close() // The example produces the following output: // U+01D160 UTF-32 = ð (U+D834 U+DD60) UTF-16
Imports System.IO Module Example4 Public Sub Main() Dim sw As New StreamWriter(".\chars2.txt") Dim utf32 As Integer = &H1D160 Dim surrogate As String = Char.ConvertFromUtf32(utf32) sw.WriteLine("U+{0:X6} UTF-32 = {1} ({2}) UTF-16", utf32, surrogate, ShowCodePoints(surrogate)) sw.Close() End Sub Private Function ShowCodePoints(value As String) As String Dim retval As String = Nothing For Each ch In value retval += String.Format("U+{0:X4} ", Convert.ToUInt16(ch)) Next Return retval.Trim() End Function End Module ' The example produces the following output: ' U+01D160 UTF-32 = ð (U+D834 U+DD60) UTF-16
Символы и категории символов
Каждый символ Юникода или допустимая суррогатная пара принадлежит к определенной категории Юникода. В .NET категории Юникода представлены элементами UnicodeCategory перечисления и включают такие значения, как UnicodeCategory.CurrencySymbol, UnicodeCategory.LowercaseLetterи UnicodeCategory.SpaceSeparator, например.
Чтобы определить категорию символа Юникода, вызовите GetUnicodeCategory метод. В следующем примере GetUnicodeCategory используется для отображения категории Юникода каждого символа в строке. Пример работает правильно, только если в экземпляре String нет суррогатных пар.
using System;
using System.Globalization;
class Example
{
public static void Main()
{
// Define a string with a variety of character categories.
String s = "The red car drove down the long, narrow, secluded road.";
// Determine the category of each character.
foreach (var ch in s)
Console.WriteLine("'{0}': {1}", ch, Char.GetUnicodeCategory(ch));
}
}
// The example displays the following output:
// 'T': UppercaseLetter
// 'h': LowercaseLetter
// 'e': LowercaseLetter
// ' ': SpaceSeparator
// 'r': LowercaseLetter
// 'e': LowercaseLetter
// 'd': LowercaseLetter
// ' ': SpaceSeparator
// 'c': LowercaseLetter
// 'a': LowercaseLetter
// 'r': LowercaseLetter
// ' ': SpaceSeparator
// 'd': LowercaseLetter
// 'r': LowercaseLetter
// 'o': LowercaseLetter
// 'v': LowercaseLetter
// 'e': LowercaseLetter
// ' ': SpaceSeparator
// 'd': LowercaseLetter
// 'o': LowercaseLetter
// 'w': LowercaseLetter
// 'n': LowercaseLetter
// ' ': SpaceSeparator
// 't': LowercaseLetter
// 'h': LowercaseLetter
// 'e': LowercaseLetter
// ' ': SpaceSeparator
// 'l': LowercaseLetter
// 'o': LowercaseLetter
// 'n': LowercaseLetter
// 'g': LowercaseLetter
// ',': OtherPunctuation
// ' ': SpaceSeparator
// 'n': LowercaseLetter
// 'a': LowercaseLetter
// 'r': LowercaseLetter
// 'r': LowercaseLetter
// 'o': LowercaseLetter
// 'w': LowercaseLetter
// ',': OtherPunctuation
// ' ': SpaceSeparator
// 's': LowercaseLetter
// 'e': LowercaseLetter
// 'c': LowercaseLetter
// 'l': LowercaseLetter
// 'u': LowercaseLetter
// 'd': LowercaseLetter
// 'e': LowercaseLetter
// 'd': LowercaseLetter
// ' ': SpaceSeparator
// 'r': LowercaseLetter
// 'o': LowercaseLetter
// 'a': LowercaseLetter
// 'd': LowercaseLetter
// '.': OtherPunctuation
open System
// Define a string with a variety of character categories.
let s = "The red car drove down the long, narrow, secluded road."
// Determine the category of each character.
for ch in s do
printfn $"'{ch}': {Char.GetUnicodeCategory ch}"
// The example displays the following output:
// 'T': UppercaseLetter
// 'h': LowercaseLetter
// 'e': LowercaseLetter
// ' ': SpaceSeparator
// 'r': LowercaseLetter
// 'e': LowercaseLetter
// 'd': LowercaseLetter
// ' ': SpaceSeparator
// 'c': LowercaseLetter
// 'a': LowercaseLetter
// 'r': LowercaseLetter
// ' ': SpaceSeparator
// 'd': LowercaseLetter
// 'r': LowercaseLetter
// 'o': LowercaseLetter
// 'v': LowercaseLetter
// 'e': LowercaseLetter
// ' ': SpaceSeparator
// 'd': LowercaseLetter
// 'o': LowercaseLetter
// 'w': LowercaseLetter
// 'n': LowercaseLetter
// ' ': SpaceSeparator
// 't': LowercaseLetter
// 'h': LowercaseLetter
// 'e': LowercaseLetter
// ' ': SpaceSeparator
// 'l': LowercaseLetter
// 'o': LowercaseLetter
// 'n': LowercaseLetter
// 'g': LowercaseLetter
// ',': OtherPunctuation
// ' ': SpaceSeparator
// 'n': LowercaseLetter
// 'a': LowercaseLetter
// 'r': LowercaseLetter
// 'r': LowercaseLetter
// 'o': LowercaseLetter
// 'w': LowercaseLetter
// ',': OtherPunctuation
// ' ': SpaceSeparator
// 's': LowercaseLetter
// 'e': LowercaseLetter
// 'c': LowercaseLetter
// 'l': LowercaseLetter
// 'u': LowercaseLetter
// 'd': LowercaseLetter
// 'e': LowercaseLetter
// 'd': LowercaseLetter
// ' ': SpaceSeparator
// 'r': LowercaseLetter
// 'o': LowercaseLetter
// 'a': LowercaseLetter
// 'd': LowercaseLetter
// '.': OtherPunctuation
Imports System.Globalization
Module Example1
Public Sub Main()
' Define a string with a variety of character categories.
Dim s As String = "The car drove down the narrow, secluded road."
' Determine the category of each character.
For Each ch In s
Console.WriteLine("'{0}': {1}", ch, Char.GetUnicodeCategory(ch))
Next
End Sub
End Module
' The example displays the following output:
' 'T': UppercaseLetter
' 'h': LowercaseLetter
' 'e': LowercaseLetter
' ' ': SpaceSeparator
' 'r': LowercaseLetter
' 'e': LowercaseLetter
' 'd': LowercaseLetter
' ' ': SpaceSeparator
' 'c': LowercaseLetter
' 'a': LowercaseLetter
' 'r': LowercaseLetter
' ' ': SpaceSeparator
' 'd': LowercaseLetter
' 'r': LowercaseLetter
' 'o': LowercaseLetter
' 'v': LowercaseLetter
' 'e': LowercaseLetter
' ' ': SpaceSeparator
' 'd': LowercaseLetter
' 'o': LowercaseLetter
' 'w': LowercaseLetter
' 'n': LowercaseLetter
' ' ': SpaceSeparator
' 't': LowercaseLetter
' 'h': LowercaseLetter
' 'e': LowercaseLetter
' ' ': SpaceSeparator
' 'l': LowercaseLetter
' 'o': LowercaseLetter
' 'n': LowercaseLetter
' 'g': LowercaseLetter
' ',': OtherPunctuation
' ' ': SpaceSeparator
' 'n': LowercaseLetter
' 'a': LowercaseLetter
' 'r': LowercaseLetter
' 'r': LowercaseLetter
' 'o': LowercaseLetter
' 'w': LowercaseLetter
' ',': OtherPunctuation
' ' ': SpaceSeparator
' 's': LowercaseLetter
' 'e': LowercaseLetter
' 'c': LowercaseLetter
' 'l': LowercaseLetter
' 'u': LowercaseLetter
' 'd': LowercaseLetter
' 'e': LowercaseLetter
' 'd': LowercaseLetter
' ' ': SpaceSeparator
' 'r': LowercaseLetter
' 'o': LowercaseLetter
' 'a': LowercaseLetter
' 'd': LowercaseLetter
' '.': OtherPunctuation
На внутреннем уровне для символов вне диапазона ASCII (от U+0000 до U+00FF) метод GetUnicodeCategory зависит от категорий Юникода, сообщаемых классом CharUnicodeInfo. Начиная с платформа .NET Framework 4.6.2 символы Юникода классифицируются по стандарту Юникода версии 8.0.0. В версиях платформа .NET Framework от платформа .NET Framework 4 до платформа .NET Framework 4.6.1 они классифицируются по стандарту Юникода версии 6.3.0.
Символы и текстовые элементы
Так как один символ может быть представлен несколькими объектами Char, не всегда имеет смысл работать с отдельными объектами Char. Например, в следующем примере кодовые точки Юникода, представляющие эгейские цифры 0–9, преобразуются в кодовые единицы в кодировке UTF-16. Так как в нем объекты Char ошибочно приравниваются к символам, он неточно сообщает, что результирующая строка содержит 20 символов.
using System;
public class Example5
{
public static void Main()
{
string result = String.Empty;
for (int ctr = 0x10107; ctr <= 0x10110; ctr++) // Range of Aegean numbers.
result += Char.ConvertFromUtf32(ctr);
Console.WriteLine("The string contains {0} characters.", result.Length);
}
}
// The example displays the following output:
// The string contains 20 characters.
open System
let result =
[ for i in 0x10107..0x10110 do // Range of Aegean numbers.
Char.ConvertFromUtf32 i ]
|> String.concat ""
printfn $"The string contains {result.Length} characters."
// The example displays the following output:
// The string contains 20 characters.
Module Example5
Public Sub Main()
Dim result As String = String.Empty
For ctr As Integer = &H10107 To &H10110 ' Range of Aegean numbers.
result += Char.ConvertFromUtf32(ctr)
Next
Console.WriteLine("The string contains {0} characters.", result.Length)
End Sub
End Module
' The example displays the following output:
' The string contains 20 characters.
Чтобы избежать предположения, что Char объект представляет один символ, можно сделать следующее:
Можно работать с объектом String целиком, а не с отдельными его символами, при представлении и анализе его лингвистического содержимого.
Вы можете использовать String.EnumerateRunes , как показано в следующем примере:
int CountLetters(string s) { int letterCount = 0; foreach (Rune rune in s.EnumerateRunes()) { if (Rune.IsLetter(rune)) { letterCount++; } } return letterCount; }
let countLetters (s: string) = let mutable letterCount = 0 for rune in s.EnumerateRunes() do if Rune.IsLetter rune then letterCount <- letterCount + 1 letterCount
Можно использовать класс StringInfo для работы с элементами текста вместо отдельных объектов Char. В следующем примере для подсчета количества элементов текста в строке, состоящей из эгейских цифр от нуля до девяти, используется объект StringInfo. Так как он считает суррогатную пару одним символом, он правильно сообщает, что строка содержит десять символов.
using System; using System.Globalization; public class Example4 { public static void Main() { string result = String.Empty; for (int ctr = 0x10107; ctr <= 0x10110; ctr++) // Range of Aegean numbers. result += Char.ConvertFromUtf32(ctr); StringInfo si = new StringInfo(result); Console.WriteLine("The string contains {0} characters.", si.LengthInTextElements); } } // The example displays the following output: // The string contains 10 characters.
open System open System.Globalization let result = [ for i in 0x10107..0x10110 do // Range of Aegean numbers. Char.ConvertFromUtf32 i ] |> String.concat "" let si = StringInfo result printfn $"The string contains {si.LengthInTextElements} characters." // The example displays the following output: // The string contains 10 characters.
Imports System.Globalization Module Example6 Public Sub Main() Dim result As String = String.Empty For ctr As Integer = &H10107 To &H10110 ' Range of Aegean numbers. result += Char.ConvertFromUtf32(ctr) Next Dim si As New StringInfo(result) Console.WriteLine("The string contains {0} characters.", si.LengthInTextElements) End Sub End Module ' The example displays the following output: ' The string contains 10 characters.
Если строка содержит базовый символ с одним или несколькими несамостоятельными знаками, можно вызвать метод String.Normalize для преобразования подстроки в одиночную кодовую единицу в кодировке UTF-16. В следующем примере метод String.Normalize используется для преобразования базового символа U+0061 (LATIN SMALL LETTER A — латинская строчная буква "a") и несамостоятельного знака U+0308 (COMBINING DIAERESIS — комбинируемое надстрочное двоеточие) в U+00E4 (LATIN SMALL LETTER A WITH DIAERESIS — латинская строчная буква "a" с диэризисом).
using System; public class Example2 { public static void Main() { string combining = "\u0061\u0308"; ShowString(combining); string normalized = combining.Normalize(); ShowString(normalized); } private static void ShowString(string s) { Console.Write("Length of string: {0} (", s.Length); for (int ctr = 0; ctr < s.Length; ctr++) { Console.Write("U+{0:X4}", Convert.ToUInt16(s[ctr])); if (ctr != s.Length - 1) Console.Write(" "); } Console.WriteLine(")\n"); } } // The example displays the following output: // Length of string: 2 (U+0061 U+0308) // // Length of string: 1 (U+00E4)
open System let showString (s: string) = printf $"Length of string: {s.Length} (" for i = 0 to s.Length - 1 do printf $"U+{Convert.ToUInt16 s[i]:X4}" if i <> s.Length - 1 then printf " " printfn ")\n" let combining = "\u0061\u0308" showString combining let normalized = combining.Normalize() showString normalized // The example displays the following output: // Length of string: 2 (U+0061 U+0308) // // Length of string: 1 (U+00E4)
Module Example3 Public Sub Main() Dim combining As String = ChrW(&H61) + ChrW(&H308) ShowString(combining) Dim normalized As String = combining.Normalize() ShowString(normalized) End Sub Private Sub ShowString(s As String) Console.Write("Length of string: {0} (", s.Length) For ctr As Integer = 0 To s.Length - 1 Console.Write("U+{0:X4}", Convert.ToUInt16(s(ctr))) If ctr <> s.Length - 1 Then Console.Write(" ") Next Console.WriteLine(")") Console.WriteLine() End Sub End Module ' The example displays the following output: ' Length of string: 2 (U+0061 U+0308) ' ' Length of string: 1 (U+00E4)
Распространенные операции
Структура Char предоставляет методы для сравнения объектов Char, преобразования значения заданного объекта Char в объект другого типа и определения категории Юникода для объекта Char:
Действие | Используйте эти методы System.Char |
---|---|
Сравнение Char объектов | CompareTo и Equals. |
Преобразование точки кода в строку | ConvertFromUtf32 См. Rune также тип. |
Преобразовать объект Char или суррогатную пару объектов Char в кодовую точку | Для одного символа: Convert.ToInt32(Char) Для суррогатной пары или символа в строке: Char.ConvertToUtf32 См. Rune также тип. |
Получение категории Юникода символа | GetUnicodeCategory См. также Rune.GetUnicodeCategory. |
Определить, принадлежит ли символ определенной категории Юникода, например: цифры, буквы, знаки препинания, управляющие символы и т. д. | IsControl, IsDigit, IsHighSurrogate, IsLetter, IsLetterOrDigit, IsLower, IsLowSurrogate, IsNumber, IsPunctuation, IsSeparator, IsSurrogate, IsSurrogatePair, IsSymbol, IsUpper и IsWhiteSpace См. также соответствующие методы для Rune типа. |
Преобразовать объект Char, который представляет число, в значение числового типа | GetNumericValue См. также Rune.GetNumericValue. |
Преобразует символ в строке в объект Char | Parse и TryParse. |
Преобразовать объект Char в объект |
ToString |
Изменение регистра объекта Char | ToLower, ToLowerInvariant, ToUpper и ToUpperInvariant См. также соответствующие методы для Rune типа. |
Значения Char и взаимодействие
Если управляемый Char тип, представленный в кодировке Юникод UTF-16, передается в неуправляемый код, маршализатор взаимодействия преобразует набор символов в ANSI по умолчанию. Можно применить атрибут DllImportAttribute к объявлениям вызова неуправляемого кода и атрибут StructLayoutAttribute к объявлениям COM-взаимодействия для управления набором символов, используемым при маршалинге типа Char.