.NET Framework
strängar
Sök…
Anmärkningar
I .NET-strängar System.String
är sekvensen med tecken System.Char
, varje tecken är en UTF-16 kodad kodenhet. Denna distinktion är viktig eftersom talad definition av karaktär och .NET (och många andra språk) definition av karaktär språk är olika.
Ett tecken , som bör kallas korrekt grafeme , det visas som en glyph och det definieras av en eller flera Unicode -kodpunkter . Varje kodpunkt kodas sedan i en sekvens av kodenheter . Nu borde det vara klart varför ett enda System.Char
representerar inte alltid ett diagram, låt oss se i verkliga världen hur de är annorlunda:
- Ett diagram kan på grund av kombination av tecken resultera i två eller flera kodpunkter: à består av två kodpunkter: U + 0061 LATIN SMALL LETTER A och U + 0300 COMBINING GRAVE ACCENT . Detta är det vanligaste misstaget eftersom
"à".Length == 2
medan du kan förvänta dig1
. - Det finns duplicerade tecken, till exempel à kan vara en enda kodpunkt U + 00E0 LATIN Liten bokstav A med grav eller två kodpunkter som förklarats ovan. Uppenbarligen måste de jämföra samma:
"\u00e0" == "\u0061\u0300"
(även om"\u00e0".Length != "\u0061\u0300".Length
). Detta är möjligt på grund av sträng normalisering utförs avString.Normalize()
metoden. - En Unicode-sekvens kan innehålla en sammansatt eller sönderdelad sekvens, till exempel tecken 한 U + D55C HAN CHARACTER kan vara en enda kodpunkt (kodad som en enda kodenhet i UTF-16) eller en sönderdelad sekvens av sina stavelser ᄒ , ᅡ och ᆫ . De måste jämföras lika.
- En kodpunkt kan kodas till mer än en kodenhet : tecken 𠂊 U + 2008A HAN CHARACTER är kodad som två
System.Char
("\ud840\udc8a"
) även om det bara är en kodpunkt: UTF-16 kodningen är inte fast storlek! Detta är en källa till otaliga buggar (även allvarliga säkerhetsbuggar), om till exempel din applikation tillämpar en maximal längd och trunkerar blind strängen på det kan du skapa en ogiltig sträng. - Vissa språk har digraph och trigraphs, till exempel i tjeckiska ch är en fristående bokstav (efter h och innan jag då när du beställer en lista med strängar kommer du att ha fyzika innan kemie .
Det finns mycket fler frågor om texthantering, se till exempel Hur kan jag utföra ett Unicode-medvetet tecken efter teckenjämförelse? för en bredare introduktion och fler länkar till relaterade argument.
Generellt när du arbetar med internationell text kan du använda denna enkla funktion för att räkna upp textelement i en sträng (undvika att bryta Unicode-surrogat och kodning):
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;
}
}
Räkna olika karaktärer
Om du behöver räkna distinkta karaktärer då de skäl som anges i Anmärkningar avsnittet kan du inte bara använda Length
egendom eftersom det är längden på rad System.Char
som inte är tecken men code-enheter (ej Unicode code-poäng inte heller grafem). Om du till exempel bara skriver text.Distinct().Count()
får du felaktiga resultat, rätt kod:
int distinctCharactersCount = text.EnumerateCharacters().Count();
Ett steg längre är att räkna förekomster av varje karaktär , om prestanda inte är ett problem kan du helt enkelt göra det så här (i det här exemplet oavsett fall):
var frequencies = text.EnumerateCharacters()
.GroupBy(x => x, StringComparer.CurrentCultureIgnoreCase)
.Select(x => new { Character = x.Key, Count = x.Count() };
Räkna tecken
Om du behöver räkna tecken då, av de skäl som förklaras i avsnittet Kommentarer , kan du inte bara använda Längdegenskapen eftersom det är längden på arrayen System.Char
som inte är tecken utan kodenheter (inte Unicode-kodpunkter eller grafem). Rätt kod är då:
int length = text.EnumerateCharacters().Count();
En liten optimering kan skriva om förlängningsmetoden EnumerateCharacters()
specifikt för detta ändamål:
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;
}
}
Räkna förekomster av en karaktär
På grund av de skäl som anges i Anmärkningar avsnittet kan du inte bara göra det (om du inte vill räkna förekomster av en specifik kod-enhet):
int count = text.Count(x => x == ch);
Du behöver en mer komplex funktion:
public static int CountOccurrencesOf(this string text, string character)
{
return text.EnumerateCharacters()
.Count(x => String.Equals(x, character, StringComparer.CurrentCulture));
}
Observera att strängjämförelse (i motsats till teckenjämförelse som är kulturinvariant) alltid måste utföras enligt regler för en specifik kultur.
Dela strängen i block med fast längd
Vi kan inte bryta en sträng i godtyckliga punkter (eftersom ett System.Char
kan inte vara giltigt enbart eftersom det är en kombinerande karaktär eller del av en surrogat) då måste kod ta hänsyn till det (notera att med längd menar jag antalet grafema inte antal kodenheter ):
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;
}
}
Konvertera sträng till / från en annan kodning
.NET-strängar innehåller System.Char
(UTF-16 kodenheter). Om du vill spara (eller hantera) text med en annan kodning måste du arbeta med en rad System.Byte
.
Omvandlingar utförs av klasser härrörande från System.Text.Encoder
och System.Text.Decoder
som tillsammans kan konvertera till / från en annan kodning (från en byte X- kodad matrisbyte byte[]
till en UTF-16-kodad System.String
och vice -versa).
Eftersom kodaren / avkodaren vanligtvis fungerar mycket nära varandra grupperas de i en klass härrörande från System.Text.Encoding
, härledda klasser erbjuder konverteringar till / från populära kodningar (UTF-8, UTF-16 och så vidare).
Exempel:
Konvertera en sträng till UTF-8
byte[] data = Encoding.UTF8.GetBytes("This is my text");
Konvertera UTF-8-data till en sträng
var text = Encoding.UTF8.GetString(data);
Ändra kodning av en befintlig textfil
Den här koden läser innehållet i en UTF-8-kodad textfil och sparar den tillbaka kodad som UTF-16. Observera att den här koden inte är optimal om filen är stor eftersom den kommer att läsa allt innehåll i minnet:
var content = File.ReadAllText(path, Encoding.UTF8);
File.WriteAllText(content, Encoding.UTF16);
Object.ToString () virtuell metod
Allt i NET är ett objekt, därav varje typ har ToString()
metod som definieras i Object
som kan åsidosättas. Standardimplementering av denna metod returnerar bara namnet på typen:
public class Foo
{
}
var foo = new Foo();
Console.WriteLine(foo); // outputs Foo
ToString()
kallas implicit när man koncentrerar värdet med en sträng:
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
Resultatet av denna metod används också i stor utsträckning av felsökningsverktyg. Om du av någon anledning inte vill åsidosätta den här metoden utan vill anpassa hur felsökare visar värdet på din typ använder du 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;}
// ...
}
Strängarnas oföränderlighet
Strängar är oföränderliga. Du kan bara inte ändra befintlig sträng. Varje operation på strängen raderar en ny instans av strängen med nytt värde. Det betyder att om du behöver ersätta ett enda tecken i en mycket lång sträng, kommer minnet att allokeras för ett nytt värde.
string veryLongString = ...
// memory is allocated
string newString = veryLongString.Remove(0,1); // removes first character of the string.
Om du behöver utföra många operationer med strängvärde använder du StringBuilder
klassen som är utformad för effektiv stränghantering:
var sb = new StringBuilder(someInitialString);
foreach(var str in manyManyStrings)
{
sb.Append(str);
}
var finalString = sb.ToString();
Omp Jämföra strängar
Trots String
är en referenstyp ==
operatör jämför jämförvärden snarare än referenser.
Som ni kanske vet är string
bara en rad tecken. Men om du tror att strängar jämställdhetskontroll och jämförelse görs karaktär för karaktär, tar du fel. Denna operation är kulturspecifik (se anmärkningar nedan): vissa karaktärsekvenser kan behandlas som lika beroende på kulturen .
Tänka två gånger innan kortslutning kontroll jämlikhet genom att jämföra Length
egenskaper av två strängar!
Använd överbelastning av String.Equals
metoden som accepterar ytterligare StringComparison
uppräkningsvärde om du behöver ändra standardbeteendet.