Jak dotąd jest to najlepsze rozwiązanie, ale do kompilacji musisz użyć \\ B. W przeciwnym razie kompilator próbuje traktować \ B jako sekwencję ucieczki.
Ferruccio
Niezłe rozwiązanie. Czy ktoś może wymyślić powód, dla którego nie powinna to być akceptowana odpowiedź? Czy jest mniej sprawny czy mniej wydajny?
Drew Noakes
8
Ten traktuje kolejne wielkie litery jako oddzielne słowa (np. ANZAC to 5 słów), podczas gdy odpowiedź MizardX traktuje je (poprawnie IMHO) jako jedno słowo.
Ray
2
@Ray, argumentowałbym, że „ANZAC” powinno być zapisane jako „Anzac”, aby było traktowane jako słowo pascalowe, ponieważ nie jest to angielskie słowo.
Sam,
1
@Neaox, po angielsku powinno być, ale to nie jest akronim-przypadek ani normalny-angielski-przypadek; jest rozdzielany wielkimi literami. Jeśli tekst źródłowy powinien być pisany wielkimi literami w taki sam sposób, jak w normalnym języku angielskim, inne litery również nie powinny być pisane wielką literą. Na przykład, dlaczego „i” w ”jest„ pisane wielką literą, aby pasowało do formatu rozdzielanego wielkimi literami, ale nie „NZAC” w „ANZAC”? Ściśle mówiąc, jeśli interpretujesz „ANZAC” jako rozdzielany wielkimi literami, będzie to 5 słów, po jednym na każdą literę.
Sam
19
Świetna odpowiedź, MizardX! Poprawiłem go nieznacznie, aby traktować cyfry jako oddzielne słowa, tak aby „Wiersz adresu1” stał się „Wiersz adresu 1” zamiast „Wiersz adresu 1”:
Świetny dodatek! Podejrzewam, że wiele osób będzie zaskoczonych obsługą liczb w łańcuchach przez zaakceptowaną odpowiedź. :)
Jordan Grey
Wiem, że minęło prawie 8 lat, odkąd to opublikowałeś, ale dla mnie też zadziałało idealnie. :) Liczby mnie zaskoczyły.
Michael Armes
Jedyna odpowiedź, która przechodzi moje dwa testy odstających: „Take5” -> „Take 5”, „PublisherID” -> „Publisher ID”. Chcę to dwukrotnie
zagłosować
18
Tylko dla małej odmiany ... Oto metoda rozszerzenia, która nie używa wyrażenia regularnego.
publicstaticclassCamelSpaceExtensions{publicstaticstringSpaceCamelCase(thisString input){returnnewstring(Enumerable.Concat(
input.Take(1),// No space before initial capInsertSpacesBeforeCaps(input.Skip(1))).ToArray());}privatestaticIEnumerable<char>InsertSpacesBeforeCaps(IEnumerable<char> input){foreach(char c in input){if(char.IsUpper(c)){yieldreturn' ';}yieldreturn c;}}}
Aby uniknąć używania Trim (), przed foreach wstawiłem: int counter = -1. wewnątrz, dodaj licznik ++. zmień czek na: if (char.IsUpper (c) && counter> 0)
Outside the Box Developer
Wstawia spację przed pierwszym znakiem.
Zar Shardan
Pozwoliłem sobie rozwiązać problem wskazany przez @ZarShardan. Jeśli nie podoba Ci się ta zmiana, możesz cofnąć lub edytować własną poprawkę.
jpmc26
Czy można to ulepszyć w celu obsługi skrótów, na przykład dodając spację przed ostatnią wielką literą w serii wielkich liter, np. BOEForecast => BOE Forecast
Nepaluz
11
Pomijając doskonały komentarz Granta Wagnera:
Dim s AsString=RegularExpressions.Regex.Replace("ThisIsMyCapsDelimitedString","([A-Z])"," $1")
Słuszna uwaga ... Nie wahaj się wstawić dowolnych plików .substring (), .trimstart (), .trim (), .remove () itp. :)
Pseudo Masochist
9
Potrzebowałem rozwiązania obsługującego akronimy i liczby. To rozwiązanie oparte na Regex traktuje następujące wzorce jako pojedyncze „słowa”:
Wielka litera, po której następują małe litery
Sekwencja kolejnych liczb
Kolejne wielkie litery (interpretowane jako akronimy) - nowe słowo może zaczynać się od ostatniej dużej litery, np. HTMLGuide => "Przewodnik HTML", "TheATeam" => "Drużyna A"
usingSystem.Text.RegularExpressions;namespaceDemo{publicclassIntercappedStringHelper{privatestaticreadonlyRegexSeparatorRegex;staticIntercappedStringHelper(){conststring pattern =@"
(?<!^) # Not start
(
# Digit, not preceded by another digit
(?<!\d)\d
|
# Upper-case letter, followed by lower-case letter if
# preceded by another upper-case letter, e.g. 'G' in HTMLGuide
(?(?<=[A-Z])[A-Z](?=[a-z])|[A-Z])
)";var options =RegexOptions.IgnorePatternWhitespace|RegexOptions.Compiled;SeparatorRegex=newRegex(pattern, options);}publicstaticstringSeparateWords(stringvalue,string separator =" "){returnSeparatorRegex.Replace(value, separator +"$1");}}}
+1 za wyjaśnienie wyrażenia regularnego i uczynienie go tak czytelnym. I nauczyłem się czegoś nowego. W .NET Regex jest dostępny tryb wolnych odstępów i komentarze. Dziękuję Ci!
Felix Keil
4
Aby uzyskać większą różnorodność, używając zwykłych starych obiektów C #, następujące dane wyjściowe dają te same dane wyjściowe, co doskonałe wyrażenie regularne @ MizardX.
publicstringFromCamelCase(string camel){// omitted checking camel for nullStringBuilder sb =newStringBuilder();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++;}elseif(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();}
Wiedziałem, że będzie łatwy sposób na RegEx ... Muszę zacząć go częściej używać.
Max Schmeling
1
Nie guru wyrażeń regularnych, ale co się dzieje z „HeresAWTFString”?
Nick
1
Dostajesz „Heres AWTF String”, ale właśnie o to zapytał Matias Nino w pytaniu.
Max Schmeling
Tak, musi dodać, że „wiele sąsiednich stolic pozostaje w spokoju”. Co jest dość oczywiste w wielu przypadkach, np. „PublisherID” oznacza tutaj „Publisher I D”, co jest okropne
PandaWood
2
Regex jest około 10-12 razy wolniejszy niż prosta pętla:
publicstaticstringCamelCaseToSpaceSeparated(thisstring str){if(string.IsNullOrEmpty(str)){return str;}var res =newStringBuilder();
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();}
Zmodyfikowałem cię, ale ludzie generalnie lepiej przyjmują klapsa, jeśli nie zaczyna się od „naiwności”.
MusiGenesis,
Nie sądzę, żeby to był klaps. W tym kontekście naiwność zwykle oznacza oczywistość lub prostotę (czyli niekoniecznie najlepsze rozwiązanie). Nie ma zamiaru obrażać.
Ferruccio,
0
Prawdopodobnie jest bardziej eleganckie rozwiązanie, ale oto, co wymyśliłem od razu:
string myString ="ThisIsMyCapsDelimitedString";for(int i =1; i < myString.Length; i++){if(myString[i].ToString().ToUpper()== myString[i].ToString()){
myString = myString.Insert(i," ");
i++;}}
privatestaticStringBuilder camelCaseToRegular(string i_String){StringBuilder output =newStringBuilder();int i =0;foreach(char character in i_String){if(character <='Z'&& character >='A'&& i >0){
output.Append(" ");}
output.Append(character);
i++;}return output;}
/// <summary>/// Get the words in a code <paramref name="identifier"/>./// </summary>/// <param name="identifier">The code <paramref name="identifier"/></param> to extract words from.publicstaticstring[]GetWords(thisstring identifier){Contract.Ensures(Contract.Result<string[]>()!=null,"returned array of string is not null but can be empty");if(identifier ==null){returnnewstring[0];}if(identifier.Length==0){returnnewstring[0];}constint MIN_WORD_LENGTH =2;// Ignore one letter or one digit wordsvar length = identifier.Length;var list =newList<string>(1+ length/2);// Set capacity, not possible more words since we discard one char wordsvar sb =newStringBuilder();CharKind cKindCurrent =GetCharKind(identifier[0]);// length is not zero hereCharKind 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 cKindCurrentswitch(cKindCurrent){caseCharKind.Digit:caseCharKind.LowerCaseLetter:
sb.Append(c);// Append digit or lowerCaseLetter to sbif(cKindNext ==CharKind.UpperCaseLetter){goto TURN_SB_INTO_WORD;// Finish word if next char is upper}goto CHAR_PROCESSED;caseCharKind.Other:goto TURN_SB_INTO_WORD;default:// charCurrent is never Start or EndDebug.Assert(cKindCurrent ==CharKind.UpperCaseLetter);break;}// Here cKindCurrent is UpperCaseLetter// Append UpperCaseLetter to sb anyway
sb.Append(c);switch(cKindNext){default:goto CHAR_PROCESSED;caseCharKind.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;caseCharKind.End:caseCharKind.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();}privatestaticCharKindGetCharKind(char c){if(char.IsDigit(c)){returnCharKind.Digit;}if(char.IsLetter(c)){if(char.IsUpper(c)){returnCharKind.UpperCaseLetter;}Debug.Assert(char.IsLower(c));returnCharKind.LowerCaseLetter;}returnCharKind.Other;}enumCharKind{End,// For end of stringDigit,UpperCaseLetter,LowerCaseLetter,Other}
Testy:
[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("abc<cde","abc cde")][TestCase("abc<>cde","abc cde")][TestCase("abc<D>cde","abc cde")]// Ignore one letter or one digit words[TestCase("abc<Da>cde","abc Da cde")][TestCase("abc<cde>","abc cde")][TestCase("SimpleHTTPServer","Simple HTTP Server")][TestCase("SimpleHTTPS2erver","Simple HTTPS2erver")][TestCase("camelCase","camel Case")][TestCase("m_Field","Field")][TestCase("mm_Field","mm Field")]publicvoidTest_GetWords(string identifier,string expectedWordsStr){var expectedWords = expectedWordsStr.Split(' ');if(identifier ==null|| identifier.Length<=1){
expectedWords =newstring[0];}var words = identifier.GetWords();Assert.IsTrue(words.SequenceEqual(expectedWords));}
Proste rozwiązanie, które powinno być o rząd (y) wielkości szybsze niż rozwiązanie regex (na podstawie testów, które przeprowadziłem z najlepszymi rozwiązaniami w tym wątku), szczególnie gdy rozmiar ciągu wejściowego rośnie:
string s1 ="ThisIsATestStringAbcDefGhiJklMnoPqrStuVwxYz";string s2;StringBuilder sb =newStringBuilder();foreach(char c in s1)
sb.Append(char.IsUpper(c)?" "+ c.ToString(): c.ToString());
s2 = sb.ToString();
Regex.Replace(s, "([A-Z0-9]+)", " $1").Trim()
. A jeśli chcesz podzielić każdą wielką literę, po prostu usuń plus.Odpowiedzi:
Zrobiłem to jakiś czas temu. Pasuje do każdego składnika nazwy CamelCase.
Na przykład:
Aby przekonwertować to, aby po prostu wstawić spacje między słowami:
Jeśli potrzebujesz obsługi cyfr:
źródło
źródło
Świetna odpowiedź, MizardX! Poprawiłem go nieznacznie, aby traktować cyfry jako oddzielne słowa, tak aby „Wiersz adresu1” stał się „Wiersz adresu 1” zamiast „Wiersz adresu 1”:
źródło
Tylko dla małej odmiany ... Oto metoda rozszerzenia, która nie używa wyrażenia regularnego.
źródło
Pomijając doskonały komentarz Granta Wagnera:
źródło
Potrzebowałem rozwiązania obsługującego akronimy i liczby. To rozwiązanie oparte na Regex traktuje następujące wzorce jako pojedyncze „słowa”:
Możesz to zrobić jako jednowierszowy:
Bardziej czytelne podejście mogłoby być lepsze:
Oto wyciąg z testów (XUnit):
źródło
Aby uzyskać większą różnorodność, używając zwykłych starych obiektów C #, następujące dane wyjściowe dają te same dane wyjściowe, co doskonałe wyrażenie regularne @ MizardX.
źródło
Poniżej znajduje się prototyp, który konwertuje następujące elementy na wielkość liter:
Oczywiście potrzebowałbyś tylko metody „ToTitleCase”.
Konsola wyglądałaby następująco:
Odwołano się do posta na blogu
źródło
źródło
Regex jest około 10-12 razy wolniejszy niż prosta pętla:
źródło
Naiwne rozwiązanie regex. Nie obsługuje O'Conner i dodaje również spację na początku łańcucha.
źródło
Prawdopodobnie jest bardziej eleganckie rozwiązanie, ale oto, co wymyśliłem od razu:
źródło
Spróbuj użyć
Wynik będzie pasował do mieszanki alfabetu z liczbami
źródło
Implementacja kodu psudo z: https://stackoverflow.com/a/5796394/4279201
źródło
Aby dopasować kategorię Unicode bez wielkich liter i wielką literą :
(?<=\P{Lu})(?=\p{Lu})
źródło
Proceduralne i szybkie impl:
Testy:
źródło
Proste rozwiązanie, które powinno być o rząd (y) wielkości szybsze niż rozwiązanie regex (na podstawie testów, które przeprowadziłem z najlepszymi rozwiązaniami w tym wątku), szczególnie gdy rozmiar ciągu wejściowego rośnie:
źródło