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 verwachten 1 .
  • 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 door String.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.



Modified text is an extract of the original Stack Overflow Documentation
Licentie onder CC BY-SA 3.0
Niet aangesloten bij Stack Overflow