.NET Framework
strings
Zoeken…
Opmerkingen
In .NET strings System.String
zijn opeenvolgende karakters System.Char
, elk karakter is een UTF-16 gecodeerde code-eenheid. Dit onderscheid is van belang omdat de gesproken taal definitie van karakter en .NET (en vele andere talen) definitie van karakter zijn verschillend.
Eén teken , dat correct grafeme zou moeten worden genoemd, wordt weergegeven als een glyph en het wordt gedefinieerd door een of meer Unicode- codepunten . Elk codepunt wordt vervolgens gecodeerd in een reeks code-eenheden . Nu moet het duidelijk zijn waarom een enkel System.Char
niet altijd een grapheme vertegenwoordigt, laten we eens kijken hoe ze anders zijn:
- Eén grafiek kan, vanwege het combineren van tekens , resulteren in twee of meer codepunten: à bestaat uit twee codepunten: U + 0061 LATIJNS KLEINE LETTER A en U + 0300 GRAVEACCENTENT COMBINEREN . Dit is de meest voorkomende fout omdat
"à".Length == 2
terwijl u mag verwachten1
. - Er zijn dubbele tekens, bijvoorbeeld à kan een enkel codepunt zijn U + 00E0 LATIJNSE KLEINE LETTER A MET GRAF of twee codepunten zoals hierboven uitgelegd. Uiteraard moeten ze hetzelfde vergelijken:
"\u00e0" == "\u0061\u0300"
(zelfs als"\u00e0".Length != "\u0061\u0300".Length
). Dit is mogelijk omdat string normalisatie uitgevoerd doorString.Normalize()
methode. - Een Unicode-reeks kan een samengestelde of ontlede reeks bevatten, bijvoorbeeld teken 한 U + D55C HAN CHARACTER kan een enkel codepunt zijn (gecodeerd als een enkele code-eenheid in UTF-16) of een ontlede reeks van de lettergrepen ᄒ , ᅡ en ᆫ . Ze moeten gelijk worden vergeleken.
- Eén codepunt kan worden gecodeerd in meer dan één code-eenheid: teken 𠂊 U + 2008A HAN CHARACTER wordt gecodeerd als twee
System.Char
("\ud840\udc8a"
), zelfs als het maar één codepunt is: UTF-16 codering heeft geen vaste grootte! Dit is een bron van talloze bugs (ook ernstige beveiligingsbugs), als uw toepassing bijvoorbeeld een maximale lengte toepast en daarop blindelings afkapt, kunt u een ongeldige string maken. - Sommige talen hebben digraph en trigraphs, bijvoorbeeld in het Tsjechisch is ch een zelfstandige letter (na h en voordat ik dan, wanneer je een lijst met strings bestelt, heb je fyzika voor chemie .
Er zijn veel meer problemen met tekstverwerking, zie bijvoorbeeld Hoe kan ik een Unicode-bewust karakter per karaktervergelijking uitvoeren? voor een bredere introductie en meer links naar gerelateerde argumenten.
Over het algemeen kunt u bij het omgaan met internationale tekst deze eenvoudige functie gebruiken om tekstelementen in een string op te sommen (vermijden om Unicode-surrogaten en codering te breken):
public static class StringExtensions
{
public static IEnumerable<string> EnumerateCharacters(this string s)
{
if (s == null)
return Enumerable.Empty<string>();
var enumerator = StringInfo.GetTextElementEnumerator(s.Normalize());
while (enumerator.MoveNext())
yield return (string)enumerator.Value;
}
}
Tel afzonderlijke karakters
Als u afzonderlijke tekens moet tellen, kunt u om de redenen die in het gedeelte Opmerkingen worden uitgelegd, niet eenvoudigweg de eigenschap Length
gebruiken omdat dit de lengte is van de array van System.Char
die geen tekens zijn maar code-eenheden (geen Unicode-codepunten) noch grafemen). Als u bijvoorbeeld gewoon tekst schrijft.Distinct text.Distinct().Count()
krijgt u onjuiste resultaten, juiste code:
int distinctCharactersCount = text.EnumerateCharacters().Count();
Een stap verder is het tellen van het aantal keren dat elk personage voorkomt , als prestaties geen probleem zijn, kunt u het gewoon zo doen (in dit voorbeeld ongeacht het geval):
var frequencies = text.EnumerateCharacters()
.GroupBy(x => x, StringComparer.CurrentCultureIgnoreCase)
.Select(x => new { Character = x.Key, Count = x.Count() };
Tekens tellen
Als u tekens moet tellen, kunt u om de redenen die worden uitgelegd in het gedeelte Opmerkingen niet eenvoudigweg de eigenschap Lengte gebruiken, omdat dit de lengte is van de array van System.Char
die geen tekens zijn, maar code-eenheden (geen Unicode-codepunten of fonemen). De juiste code is dan:
int length = text.EnumerateCharacters().Count();
Een kleine optimalisatie kan de uitbreidingsmethode EnumerateCharacters()
specifiek voor dit doel herschrijven:
public static class StringExtensions
{
public static int CountCharacters(this string text)
{
if (String.IsNullOrEmpty(text))
return 0;
int count = 0;
var enumerator = StringInfo.GetTextElementEnumerator(text);
while (enumerator.MoveNext())
++count;
return count;
}
}
Tel exemplaren van een personage
Vanwege de redenen die in de sectie Opmerkingen worden uitgelegd, kunt u dit niet eenvoudigweg doen (tenzij u exemplaren van een specifieke code-eenheid wilt tellen):
int count = text.Count(x => x == ch);
U hebt een complexere functie nodig:
public static int CountOccurrencesOf(this string text, string character)
{
return text.EnumerateCharacters()
.Count(x => String.Equals(x, character, StringComparer.CurrentCulture));
}
Merk op dat stringvergelijking (in tegenstelling tot karaktervergelijking die cultuurinvariant is) altijd moet worden uitgevoerd volgens regels voor een specifieke cultuur.
Splitstring in blokken met een vaste lengte
We kunnen een string niet in willekeurige punten splitsen (omdat een System.Char
mogelijk niet alleen geldig is omdat het een combinerend karakter of een deel van een surrogaat is), dan moet de code daar rekening mee houden (merk op dat ik met lengte het aantal grafemen bedoel, niet de aantal code-eenheden ):
public static IEnumerable<string> Split(this string value, int desiredLength)
{
var characters = StringInfo.GetTextElementEnumerator(value);
while (characters.MoveNext())
yield return String.Concat(Take(characters, desiredLength));
}
private static IEnumerable<string> Take(TextElementEnumerator enumerator, int count)
{
for (int i = 0; i < count; ++i)
{
yield return (string)enumerator.Current;
if (!enumerator.MoveNext())
yield break;
}
}
Converteer string naar / van een andere codering
.NET-strings bevatten System.Char
(UTF-16 code-eenheden). Als u tekst met een andere codering wilt opslaan (of beheren), moet u met een reeks System.Byte
.
Conversies worden uitgevoerd door klassen afgeleid van System.Text.Encoder
en System.Text.Decoder
die samen kunnen converteren naar / van een andere codering (van een byte X gecodeerde array- byte[]
naar een UTF-16 gecodeerd System.String
en vice -versa).
Omdat de encoder / decoder meestal heel dicht bij elkaar werkt, zijn ze gegroepeerd in een klasse die is afgeleid van System.Text.Encoding
, afgeleide klassen bieden conversies van / naar populaire coderingen (UTF-8, UTF-16 enzovoort).
Voorbeelden:
Converteer een string naar UTF-8
byte[] data = Encoding.UTF8.GetBytes("This is my text");
Converteer UTF-8-gegevens naar een string
var text = Encoding.UTF8.GetString(data);
Wijzig de codering van een bestaand tekstbestand
Deze code leest de inhoud van een UTF-8 gecodeerd tekstbestand en slaat het terug gecodeerd op als UTF-16. Merk op dat deze code niet optimaal is als het bestand groot is, omdat het alle inhoud in het geheugen leest:
var content = File.ReadAllText(path, Encoding.UTF8);
File.WriteAllText(content, Encoding.UTF16);
Virtuele methode Object.ToString ()
Alles NET is een object, waardoor elk type heeft ToString()
methode gedefinieerd Object
klasse die kan worden overschreven. Standaardimplementatie van deze methode retourneert alleen de naam van het type:
public class Foo
{
}
var foo = new Foo();
Console.WriteLine(foo); // outputs Foo
ToString()
wordt impliciet aangeroepen bij het samenvoegen van een waarde met een tekenreeks:
public class Foo
{
public override string ToString()
{
return "I am Foo";
}
}
var foo = new Foo();
Console.WriteLine("I am bar and "+foo);// outputs I am bar and I am Foo
Het resultaat van deze methode wordt ook uitgebreid gebruikt door hulpprogramma's voor foutopsporing. Als u om een of andere reden deze methode niet wilt overschrijven, maar wilt aanpassen hoe debugger de waarde van uw type toont, gebruikt u DebuggerDisplay Attribute ( MSDN ):
// [DebuggerDisplay("Person = FN {FirstName}, LN {LastName}")]
[DebuggerDisplay("Person = FN {"+nameof(Person.FirstName)+"}, LN {"+nameof(Person.LastName)+"}")]
public class Person
{
public string FirstName { get; set; }
public string LastName { get; set;}
// ...
}
Onveranderlijkheid van strings
Strings zijn onveranderlijk. Je kunt de bestaande string niet veranderen. Elke bewerking op de string maakt een nieuwe instantie van de string met een nieuwe waarde. Het betekent dat als u een enkel teken in een zeer lange reeks moet vervangen, geheugen wordt toegewezen voor een nieuwe waarde.
string veryLongString = ...
// memory is allocated
string newString = veryLongString.Remove(0,1); // removes first character of the string.
Als u veel bewerkingen met tekenreekswaarde moet uitvoeren, gebruikt u de klasse StringBuilder
die is ontworpen voor efficiënte manipulatie van tekenreeksen:
var sb = new StringBuilder(someInitialString);
foreach(var str in manyManyStrings)
{
sb.Append(str);
}
var finalString = sb.ToString();
Snaren vergelijken
Ondanks String
is een referentietype ==
operator vergelijkt stringwaarden in plaats van verwijzingen.
Zoals je misschien weet is string
alleen maar een reeks tekens. Maar als u denkt dat tekenreeksen gelijkheid controleren en vergelijken karakter per karakter wordt gemaakt, vergist u zich. Deze bewerking is cultuurspecifiek (zie opmerkingen hieronder): sommige tekenreeksen kunnen als gelijk worden behandeld, afhankelijk van de cultuur .
Denk twee keer na voordat kortsluiting gelijkheidstest door het vergelijken van Length
eigenschappen van twee strings!
Gebruik overbelastingen van de String.Equals
methode die extra StringComparison
opsommingswaarde accepteert als u het standaardgedrag moet wijzigen.