Ricerca…


Osservazioni

Nelle stringhe .NET System.String sono sequenze di caratteri System.Char , ogni carattere è un'unità di codice codificata UTF-16. Questa distinzione è importante perché la definizione del linguaggio parlata di carattere e la definizione di carattere .NET (e molte altre lingue) sono diverse.

Un carattere , che dovrebbe essere correttamente chiamato grafema , viene visualizzato come un glifo ed è definito da uno o più punti di codice Unicode. Ogni punto di codice è quindi codificato in una sequenza di unità di codice . Ora dovrebbe essere chiaro il motivo per cui un singolo System.Char non rappresenta sempre un grapheme, vediamo nel mondo reale come sono diversi:

  • Un grafema, a causa della combinazione di caratteri , può risultare in due o più punti di codice: à è composto da due punti di codice: U + 0061 LATIN LETTER A e U + 0300 LIVIN COMBINING GRAVE ACCENT . Questo è l'errore più comune perché "à".Length == 2 mentre ci si può aspettare 1 .
  • Ci sono caratteri duplicati, ad esempio à può essere un singolo punto di codice U + 00E0 LATIN SMALL LETTER A WITH GRAVE o due code-point come spiegato sopra. Ovviamente devono confrontare lo stesso: "\u00e0" == "\u0061\u0300" (anche se "\u00e0".Length != "\u0061\u0300".Length ). Ciò è possibile a causa della normalizzazione delle stringhe eseguita dal metodo String.Normalize() .
  • Una sequenza Unicode può contenere una sequenza composta o scomposta, per esempio il carattere U + D55C HAN CHARACTER può essere un singolo punto di codice (codificato come una singola unità di codice in UTF-16) o una sequenza decomposta delle sue sillabe , e . Devono essere paragonati allo stesso modo.
  • Un punto di codice può essere codificato su più di una unità di codice: carattere 𠂊 U + 2008A HAN CHARACTER è codificato come due System.Char ( "\ud840\udc8a" ) anche se è solo un punto di codice: UTF-16 la codifica non è una dimensione fissa! Questa è una fonte di innumerevoli bachi (anche gravi bug di sicurezza), se per esempio la tua applicazione applica una lunghezza massima e ciecamente tronca una stringa in quel momento, puoi creare una stringa non valida.
  • Alcune lingue hanno digraph e trigrammi, per esempio in ch ceco è una lettera standalone (dopo le ore e prima che io poi quando si ordina una lista di stringhe si dovrà fyzika prima Chemie.

Ci sono molti più problemi sulla gestione del testo, vedi per esempio Come posso eseguire un confronto con caratteri Unicode per confronto di caratteri? per un'introduzione più ampia e più collegamenti a argomenti correlati.

In generale, quando si tratta di testo internazionale , è possibile utilizzare questa semplice funzione per enumerare gli elementi di testo in una stringa (evitando di interrompere i surrogati e la codifica Unicode):

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;
    }
}

Conta personaggi distinti

Se hai bisogno di contare caratteri distinti, per le ragioni spiegate nella sezione Commenti , non puoi semplicemente usare la proprietà Length perché è la lunghezza dell'array di System.Char che non sono caratteri ma code-unità (non code point Unicode) né grafemi). Se, ad esempio, scrivi semplicemente text.Distinct().Count() otterrai risultati errati, codice corretto:

int distinctCharactersCount = text.EnumerateCharacters().Count();

Un passo ulteriore è contare le occorrenze di ogni carattere , se le prestazioni non sono un problema, puoi semplicemente farlo in questo modo (in questo esempio, indipendentemente dal caso):

var frequencies = text.EnumerateCharacters()
    .GroupBy(x => x, StringComparer.CurrentCultureIgnoreCase)
    .Select(x => new { Character = x.Key, Count = x.Count() };

Conta personaggi

Se hai bisogno di contare i caratteri , per le ragioni spiegate nella sezione Commenti , non puoi semplicemente usare la proprietà Length perché è la lunghezza dell'array di System.Char che non sono caratteri ma code-unità (non code-points Unicode, né grafemi). Il codice corretto è quindi:

int length = text.EnumerateCharacters().Count();

Una piccola ottimizzazione può riscrivere il metodo di estensione EnumerateCharacters() appositamente per questo scopo:

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;
    }
}

Conta le occorrenze di un personaggio

A causa delle ragioni spiegate nella sezione Commenti , non è possibile farlo semplicemente (a meno che non si vogliano contare le occorrenze di una specifica unità di codice):

int count = text.Count(x => x == ch);

Hai bisogno di una funzione più complessa:

public static int CountOccurrencesOf(this string text, string character)
{
    return text.EnumerateCharacters()
        .Count(x => String.Equals(x, character, StringComparer.CurrentCulture));
}

Si noti che il confronto delle stringhe (a differenza del confronto dei caratteri, che è invariante di cultura) deve sempre essere eseguito secondo le regole di una cultura specifica.

Dividere la stringa in blocchi di lunghezza fissa

Non possiamo spezzare una stringa in punti arbitrari (perché un System.Char può non essere valido solo perché è un personaggio che combina o parte di un surrogato) quindi il codice deve tenerne conto (notare che con lunghezza intendo il numero di grafemi non il numero di unità di codice ):

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;
    }
}

Converti una stringa in / da un'altra codifica

Le stringhe .NET contengono System.Char (unità di codice UTF-16). Se vuoi salvare (o gestire) il testo con un'altra codifica, devi lavorare con un array di System.Byte .

Le conversioni vengono eseguite da classi derivate da System.Text.Encoder e System.Text.Decoder che, insieme, possono convertire in / da un'altra codifica (da un byte X byte codificato in byte[] a un System.String e vice codificati in UTF-16 -versa).

Poiché il codificatore / decodificatore di solito funziona molto vicino tra loro, vengono raggruppati in una classe derivata da System.Text.Encoding , le classi derivate offrono conversioni alle / dalle codifiche più comuni (UTF-8, UTF-16 e così via).

Esempi:

Convertire una stringa in UTF-8

byte[] data = Encoding.UTF8.GetBytes("This is my text");

Converti dati UTF-8 in una stringa

var text = Encoding.UTF8.GetString(data);

Cambia la codifica di un file di testo esistente

Questo codice leggerà il contenuto di un file di testo con codifica UTF-8 e lo salverà codificato come UTF-16. Nota che questo codice non è ottimale se il file è grande perché leggerà tutto il suo contenuto in memoria:

var content = File.ReadAllText(path, Encoding.UTF8);
File.WriteAllText(content, Encoding.UTF16);

Object.ToString () metodo virtuale

Tutto in .NET è un oggetto, quindi ogni tipo ha il metodo ToString() definito nella classe Object che può essere sovrascritto. L'implementazione predefinita di questo metodo restituisce semplicemente il nome del tipo:

public class Foo
{
}

var foo = new Foo();
Console.WriteLine(foo); // outputs Foo

ToString() viene chiamato implicitamente quando si concatena il valore con una stringa:

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

Il risultato di questo metodo è anche ampiamente utilizzato dagli strumenti di debug. Se, per qualche motivo, non si vuole sovrascrivere questo metodo, ma si desidera personalizzare il modo in cui il debugger mostra il valore del proprio tipo, utilizzare 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;}
    // ...
}

Immutabilità delle stringhe

Le stringhe sono immutabili. Non puoi semplicemente cambiare la stringa esistente. Qualsiasi operazione sulla stringa crea una nuova istanza della stringa con un nuovo valore. Significa che se è necessario sostituire un singolo carattere in una stringa molto lunga, la memoria verrà allocata per un nuovo valore.

string veryLongString = ...
// memory is allocated
string newString = veryLongString.Remove(0,1); // removes first character of the string.

Se è necessario eseguire molte operazioni con valore stringa, utilizzare la classe StringBuilder che è progettata per una manipolazione efficiente delle stringhe:

var sb = new StringBuilder(someInitialString);
foreach(var str in manyManyStrings)
{
    sb.Append(str);
} 
var finalString = sb.ToString();

Ompare le corde

Nonostante String sia un operatore di riferimento, l'operatore == confronta i valori stringa anziché i riferimenti.

Come forse saprai, la string è solo una serie di personaggi. Ma se pensi che il controllo e il paragone delle stringhe siano fatti carattere per carattere, ti stai sbagliando. Questa operazione è specifica per la cultura (vedi Note sotto): alcune sequenze di caratteri possono essere considerate uguali a seconda della cultura .

Pensaci due volte prima di corteggiare il controllo di uguaglianza confrontando le proprietà di Length di due stringhe!

Utilizzare il sovraccarico del metodo String.Equals che accetta il valore di enumerazione StringComparison aggiuntivo, se è necessario modificare il comportamento predefinito.



Modified text is an extract of the original Stack Overflow Documentation
Autorizzato sotto CC BY-SA 3.0
Non affiliato con Stack Overflow