.NET – Come si può dividere una stringa delimitata da “maiuscole” in una matrice?

Come vado da questa stringa: “ThisIsMyCapsDelimitedString”

… a questa stringa: “This Is My Caps Delimited String”

Le righe di codice più semplici in VB.net sono preferibili, ma C # è anche il benvenuto.

Saluti!

L’ho fatto un po ‘di tempo fa. Corrisponde a ciascun componente di un nome CamelCase.

/([AZ]+(?=$|[AZ][az])|[AZ]?[az]+)/g 

Per esempio:

 "SimpleHTTPServer" => ["Simple", "HTTP", "Server"] "camelCase" => ["camel", "Case"] 

Per convertirlo basta inserire spazi tra le parole:

 Regex.Replace(s, "([az](?=[AZ])|[AZ](?=[AZ][az]))", "$1 ") 

Se devi gestire le cifre:

 /([AZ]+(?=$|[AZ][az]|[0-9])|[AZ]?[az]+|[0-9]+)/g Regex.Replace(s,"([az](?=[AZ]|[0-9])|[AZ](?=[AZ][az]|[0-9])|[0-9](?=[^0-9]))","$1 ") 
 Regex.Replace("ThisIsMyCapsDelimitedString", "(\\B[AZ])", " $1") 

Ottima risposta, MizardX! L’ho ottimizzato leggermente per trattare i numeri come parole separate, in modo che “AddressLine1” diventasse “Address Line 1” anziché “Address Line1”:

 Regex.Replace(s, "([az](?=[A-Z0-9])|[AZ](?=[AZ][az]))", "$1 ") 

Solo per una piccola varietà … Ecco un metodo di estensione che non usa un’espressione regolare.

 public static class CamelSpaceExtensions { public static string SpaceCamelCase(this String input) { return new string(InsertSpacesBeforeCaps(input).ToArray()); } private static IEnumerable InsertSpacesBeforeCaps(IEnumerable input) { foreach (char c in input) { if (char.IsUpper(c)) { yield return ' '; } yield return c; } } } 

L’eccellente commento di Grant Wagner a parte:

 Dim s As String = RegularExpressions.Regex.Replace("ThisIsMyCapsDelimitedString", "([AZ])", " $1") 

Avevo bisogno di una soluzione che supporti acronimi e numeri. Questa soluzione basata su Regex tratta i seguenti schemi come singole “parole”:

  • Una lettera maiuscola seguita da lettere minuscole
  • Una sequenza di numeri consecutivi
  • Lettere maiuscole consecutive (interpretate come acronimi) – una nuova parola può iniziare utilizzando l’ultimo capitale, ad esempio HTMLGuide => “Guida HTML”, “TheATeam” => “The A Team”

Potresti farlo come un solitario:

 Regex.Replace(value, @"(? 

Un approccio più leggibile potrebbe essere migliore:

 using System.Text.RegularExpressions; namespace Demo { public class IntercappedStringHelper { private static readonly Regex SeparatorRegex; static IntercappedStringHelper() { const string pattern = @" (? 

Ecco un estratto dai test (XUnit):

 [Theory] [InlineData("PurchaseOrders", "Purchase-Orders")] [InlineData("purchaseOrders", "purchase-Orders")] [InlineData("2Unlimited", "2-Unlimited")] [InlineData("The2Unlimited", "The-2-Unlimited")] [InlineData("Unlimited2", "Unlimited-2")] [InlineData("222Unlimited", "222-Unlimited")] [InlineData("The222Unlimited", "The-222-Unlimited")] [InlineData("Unlimited222", "Unlimited-222")] [InlineData("ATeam", "A-Team")] [InlineData("TheATeam", "The-A-Team")] [InlineData("TeamA", "Team-A")] [InlineData("HTMLGuide", "HTML-Guide")] [InlineData("TheHTMLGuide", "The-HTML-Guide")] [InlineData("TheGuideToHTML", "The-Guide-To-HTML")] [InlineData("HTMLGuide5", "HTML-Guide-5")] [InlineData("TheHTML5Guide", "The-HTML-5-Guide")] [InlineData("TheGuideToHTML5", "The-Guide-To-HTML-5")] [InlineData("TheUKAllStars", "The-UK-All-Stars")] [InlineData("AllStarsUK", "All-Stars-UK")] [InlineData("UKAllStars", "UK-All-Stars")] 

Per maggiore varietà, usando semplici vecchi oggetti C #, quanto segue produce lo stesso risultato dell’eccellente espressione regolare di @MizardX.

 public string FromCamelCase(string camel) { // omitted checking camel for null StringBuilder sb = new StringBuilder(); int upperCaseRun = 0; foreach (char c in camel) { // append a space only if we're not at the start // and we're not already in an all caps string. if (char.IsUpper(c)) { if (upperCaseRun == 0 && sb.Length != 0) { sb.Append(' '); } upperCaseRun++; } else if( char.IsLower(c) ) { if (upperCaseRun > 1) //The first new word will also be capitalized. { sb.Insert(sb.Length - 1, ' '); } upperCaseRun = 0; } else { upperCaseRun = 0; } sb.Append(c); } return sb.ToString(); } 

Di seguito è riportato un prototipo che converte quanto segue in Title Case:

  • snake_case
  • camelCase
  • PascalCase
  • frase
  • Titolo caso (mantenere la formattazione corrente)

Ovviamente, avresti solo bisogno del metodo “ToTitleCase”.

 using System; using System.Collections.Generic; using System.Globalization; using System.Text.RegularExpressions; public class Program { public static void Main() { var examples = new List { "THEQuickBrownFox", "theQUICKBrownFox", "TheQuickBrownFOX", "TheQuickBrownFox", "the_quick_brown_fox", "theFOX", "FOX", "QUICK" }; foreach (var example in examples) { Console.WriteLine(ToTitleCase(example)); } } private static string ToTitleCase(string example) { var fromSnakeCase = example.Replace("_", " "); var lowerToUpper = Regex.Replace(fromSnakeCase, @"(\p{Ll})(\p{Lu})", "$1 $2"); var sentenceCase = Regex.Replace(lowerToUpper, @"(\p{Lu}+)(\p{Lu}\p{Ll})", "$1 $2"); return new CultureInfo("en-US", false).TextInfo.ToTitleCase(sentenceCase); } } 

La console fuori sarebbe come segue:

 THE Quick Brown Fox The QUICK Brown Fox The Quick Brown FOX The Quick Brown Fox The Quick Brown Fox The FOX FOX QUICK 

Post di riferimento del blog

 string s = "ThisIsMyCapsDelimitedString"; string t = Regex.Replace(s, "([AZ])", " $1").Substring(1); 

Regex è circa 10-12 volte più lento di un semplice loop:

  public static string CamelCaseToSpaceSeparated(this string str) { if (string.IsNullOrEmpty(str)) { return str; } var res = new StringBuilder(); res.Append(str[0]); for (var i = 1; i < str.Length; i++) { if (char.IsUpper(str[i])) { res.Append(' '); } res.Append(str[i]); } return res.ToString(); } 

Soluzione regex ingenua. Non gestirà O’Conner e aggiunge uno spazio all’inizio della stringa.

 s = "ThisIsMyCapsDelimitedString" split = Regex.Replace(s, "[A-Z0-9]", " $&"); 

Probabilmente c’è una soluzione più elegante, ma questo è quello che mi viene in mente:

 string myString = "ThisIsMyCapsDelimitedString"; for (int i = 1; i < myString.Length; i++) { if (myString[i].ToString().ToUpper() == myString[i].ToString()) { myString = myString.Insert(i, " "); i++; } } 

Prova ad usare

 "([AZ]*[^AZ]*)" 

Il risultato si adatterà al mix alfabetico con i numeri

 Regex.Replace("AbcDefGH123Weh", "([AZ]*[^AZ]*)", "$1 "); Abc Def GH123 Weh Regex.Replace("camelCase", "([AZ]*[^AZ]*)", "$1 "); camel Case 

Implementazione del codice psudo da: https://stackoverflow.com/a/5796394/4279201

  private static StringBuilder camelCaseToRegular(string i_String) { StringBuilder output = new StringBuilder(); int i = 0; foreach (char character in i_String) { if (character <= 'Z' && character >= 'A' && i > 0) { output.Append(" "); } output.Append(character); i++; } return output; } 

Per far corrispondere tra lettere maiuscole e lettere maiuscole Unicode : (?<=\P{Lu})(?=\p{Lu})

 Dim s = Regex.Replace("CorrectHorseBatteryStaple", "(?<=\P{Lu})(?=\p{Lu})", " ") 

Implant procedurale e veloce:

  ///  /// Get the words in a code . ///  /// The code  to extract words from. public static string[] GetWords(this string identifier) { Contract.Ensures(Contract.Result() != null, "returned array of string is not null but can be empty"); if (identifier == null) { return new string[0]; } if (identifier.Length == 0) { return new string[0]; } const int MIN_WORD_LENGTH = 2; // Ignore one letter or one digit words var length = identifier.Length; var list = new List(1 + length/2); // Set capacity, not possible more words since we discard one char words var sb = new StringBuilder(); CharKind cKindCurrent = GetCharKind(identifier[0]); // length is not zero here CharKind cKindNext = length == 1 ? CharKind.End : GetCharKind(identifier[1]); for (var i = 0; i < length; i++) { var c = identifier[i]; CharKind cKindNextNext = (i >= length - 2) ? CharKind.End : GetCharKind(identifier[i + 2]); // Process cKindCurrent switch (cKindCurrent) { case CharKind.Digit: case CharKind.LowerCaseLetter: sb.Append(c); // Append digit or lowerCaseLetter to sb if (cKindNext == CharKind.UpperCaseLetter) { goto TURN_SB_INTO_WORD; // Finish word if next char is upper } goto CHAR_PROCESSED; case CharKind.Other: goto TURN_SB_INTO_WORD; default: // charCurrent is never Start or End Debug.Assert(cKindCurrent == CharKind.UpperCaseLetter); break; } // Here cKindCurrent is UpperCaseLetter // Append UpperCaseLetter to sb anyway sb.Append(c); switch (cKindNext) { default: goto CHAR_PROCESSED; case CharKind.UpperCaseLetter: // "SimpleHTTPServer" when we are at 'P' we need to see that NextNext is 'e' to get the word! if (cKindNextNext == CharKind.LowerCaseLetter) { goto TURN_SB_INTO_WORD; } goto CHAR_PROCESSED; case CharKind.End: case CharKind.Other: break; // goto TURN_SB_INTO_WORD; } //------------------------------------------------ TURN_SB_INTO_WORD: string word = sb.ToString(); sb.Length = 0; if (word.Length >= MIN_WORD_LENGTH) { list.Add(word); } CHAR_PROCESSED: // Shift left for next iteration! cKindCurrent = cKindNext; cKindNext = cKindNextNext; } string lastWord = sb.ToString(); if (lastWord.Length >= MIN_WORD_LENGTH) { list.Add(lastWord); } return list.ToArray(); } private static CharKind GetCharKind(char c) { if (char.IsDigit(c)) { return CharKind.Digit; } if (char.IsLetter(c)) { if (char.IsUpper(c)) { return CharKind.UpperCaseLetter; } Debug.Assert(char.IsLower(c)); return CharKind.LowerCaseLetter; } return CharKind.Other; } enum CharKind { End, // For end of string Digit, UpperCaseLetter, LowerCaseLetter, Other } 

test:

  [TestCase((string)null, "")] [TestCase("", "")] // Ignore one letter or one digit words [TestCase("A", "")] [TestCase("4", "")] [TestCase("_", "")] [TestCase("Word_m_Field", "Word Field")] [TestCase("Word_4_Field", "Word Field")] [TestCase("a4", "a4")] [TestCase("ABC", "ABC")] [TestCase("abc", "abc")] [TestCase("AbCd", "Ab Cd")] [TestCase("AbcCde", "Abc Cde")] [TestCase("ABCCde", "ABC Cde")] [TestCase("Abc42Cde", "Abc42 Cde")] [TestCase("Abc42cde", "Abc42cde")] [TestCase("ABC42Cde", "ABC42 Cde")] [TestCase("42ABC", "42 ABC")] [TestCase("42abc", "42abc")] [TestCase("abc_cde", "abc cde")] [TestCase("Abc_Cde", "Abc Cde")] [TestCase("_Abc__Cde_", "Abc Cde")] [TestCase("ABC_CDE_FGH", "ABC CDE FGH")] [TestCase("ABC CDE FGH", "ABC CDE FGH")] // Should not happend (white char) anything that is not a letter/digit/'_' is considered as a separator [TestCase("ABC,CDE;FGH", "ABC CDE FGH")] // Should not happend (,;) anything that is not a letter/digit/'_' is considered as a separator [TestCase("abccde", "abc cde")] [TestCase("abccde", "abc cde")] // Ignore one letter or one digit words [TestCase("abccde", "abc Da cde")] [TestCase("abc", "abc cde")] [TestCase("SimpleHTTPServer", "Simple HTTP Server")] [TestCase("SimpleHTTPS2erver", "Simple HTTPS2erver")] [TestCase("camelCase", "camel Case")] [TestCase("m_Field", "Field")] [TestCase("mm_Field", "mm Field")] public void Test_GetWords(string identifier, string expectedWordsStr) { var expectedWords = expectedWordsStr.Split(' '); if (identifier == null || identifier.Length <= 1) { expectedWords = new string[0]; } var words = identifier.GetWords(); Assert.IsTrue(words.SequenceEqual(expectedWords)); }