Que se passe-t-il lorsque vous avez affaire à "OldMacDonaldAndMrO'TooleWentToMcDonalds"?
Grant Wagner
2
Il ne verra qu'une utilisation limitée. Je vais principalement l'utiliser pour analyser les noms de variables tels que ThisIsMySpecialVariable,
Matias Nino
Cela a fonctionné pour moi: Regex.Replace(s, "([A-Z0-9]+)", " $1").Trim(). Et si vous souhaitez diviser sur chaque lettre majuscule, supprimez simplement le plus.
Mladen B.
Réponses:
173
J'ai fait ça il y a quelque temps. Il correspond à chaque composant d'un nom CamelCase.
Affaire de chameau! C'est comme ça que ça s'appelait! J'aime cela! Merci beaucoup!
Matias Nino
19
En fait, camelCase a une lettre minuscule en tête. Ce à quoi vous faites référence ici, c'est PascalCase.
Drew Noakes le
12
... et quand vous faites référence à quelque chose qui peut être "cas de chameau" ou "cas de pascal", cela s'appelle "intercalé"
Chris
Ne divise pas "Take5", ce qui échouerait mon cas d'utilisation
PandaWood
1
@PandaWood Digits n'était pas dans la question, donc ma réponse n'en a pas tenu compte. J'ai ajouté une variante des modèles qui tient compte des chiffres.
C'est la meilleure solution à ce jour, mais vous devez utiliser \\ B pour compiler. Sinon, le compilateur essaie de traiter le \ B comme une séquence d'échappement.
Ferruccio
Belle solution. Quelqu'un peut-il penser à une raison pour laquelle cela ne devrait pas être la réponse acceptée? Est-ce moins capable ou moins performant?
Drew Noakes
8
Celui-ci traite les majuscules consécutives comme des mots séparés (par exemple, ANZAC est de 5 mots) alors que la réponse de MizardX le traite (correctement à mon humble avis) comme un mot.
Ray
2
@Ray, je dirais que "ANZAC" devrait être écrit comme "Anzac" pour être considéré comme un mot en cas pascal puisque ce n'est pas en anglais.
Sam le
1
@Neaox, en anglais ça devrait l'être, mais ce n'est pas acronyme-case ou normal-english-case; il est délimité par des majuscules. Si le texte source doit être mis en majuscule de la même manière qu'il l'est en anglais normal, les autres lettres ne doivent pas non plus être en majuscule. Par exemple, pourquoi le «i» de «is» doit-il être mis en majuscule pour correspondre au format délimité par des majuscules, mais pas le «NZAC» de «ANZAC»? Strictement parlant, si vous interprétez "ANZAC" comme délimité par des majuscules, alors il s'agit de 5 mots, un pour chaque lettre.
Sam
19
Excellente réponse, MizardX! Je l'ai légèrement modifié pour traiter les chiffres comme des mots séparés, de sorte que "AddressLine1" devienne "Address Line 1" au lieu de "Address Line1":
Excellent ajout! Je soupçonne que peu de gens seront surpris par la gestion acceptée des nombres dans les chaînes. :)
Jordan Grey
Je sais que cela fait presque 8 ans que vous avez publié ceci, mais cela a fonctionné parfaitement pour moi aussi. :) Les chiffres m'ont fait trébucher au début.
Michael Armes
La seule réponse qui passe mes 2 tests aberrants: "Take5" -> "Take 5", "PublisherID" -> "Publisher ID". Je veux voter deux fois
PandaWood
18
Juste pour un peu de variété ... Voici une méthode d'extension qui n'utilise pas de regex.
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;}}}
Pour éviter d'utiliser Trim (), avant le foreach je mets: int counter = -1. à l'intérieur, ajoutez counter ++. changez le chèque en: if (char.IsUpper (c) && counter> 0)
Outside the Box Developer
Ceci insère un espace avant le 1er caractère.
Zar Shardan
J'ai pris la liberté de résoudre le problème signalé par @ZarShardan. N'hésitez pas à revenir en arrière ou à modifier votre propre correctif si vous n'aimez pas la modification.
jpmc26
Cela peut-il être amélioré pour gérer les abréviations, par exemple en ajoutant un espace avant la dernière majuscule dans une série de lettres majuscules, par exemple BOEForecast => BOE Forecast
Nepaluz
11
Mis à part l'excellent commentaire de Grant Wagner:
Dim s AsString=RegularExpressions.Regex.Replace("ThisIsMyCapsDelimitedString","([A-Z])"," $1")
Bon point ... N'hésitez pas à insérer les .substring (), .trimstart (), .trim (), .remove (), etc. de votre choix. :)
Pseudo masochiste
9
J'avais besoin d'une solution prenant en charge les acronymes et les nombres. Cette solution basée sur Regex traite les modèles suivants comme des «mots» individuels:
Une lettre majuscule suivie de lettres minuscules
Une séquence de nombres consécutifs
Majuscules consécutives (interprétées comme des acronymes) - un nouveau mot peut commencer en utilisant la dernière majuscule, par exemple HTMLGuide => "HTML Guide", "TheATeam" => "The A Team"
Une approche plus lisible pourrait être meilleure:
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 pour expliquer le regex et le rendre lisible. Et j'ai appris quelque chose de nouveau. Il existe un mode d'espacement libre et des commentaires dans .NET Regex. Je vous remercie!
Felix Keil
4
Pour plus de variété, en utilisant des objets C # anciens, ce qui suit produit le même résultat que l'excellente expression régulière de @ 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();}
Je savais qu'il y aurait un moyen RegEx simple ... Je dois commencer à l'utiliser davantage.
Max Schmeling
1
Pas un gourou de regex mais que se passe-t-il avec "HeresAWTFString"?
Nick
1
Vous obtenez "Heres AWTF String" mais c'est exactement ce que Matias Nino a demandé dans la question.
Max Schmeling
Ouais, il doit ajouter que "plusieurs capitales adjacentes sont laissées seules". Ce qui est assez évidemment nécessaire dans de nombreux cas, par exemple "PublisherID" va ici à "Publisher I D" qui est horrible
PandaWood
2
Regex est environ 10 à 12 fois plus lent qu'une simple boucle:
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();}
Je vous ai modifié, mais les gens acceptent généralement mieux une claque si elle ne commence pas par «naïf».
MusiGenesis
Je ne pense pas que ce soit une claque. Dans ce contexte, naïf signifie généralement évident ou simple (c'est-à-dire pas nécessairement la meilleure solution). Il n'y a aucune intention d'insulte.
Ferruccio
0
Il y a probablement une solution plus élégante, mais c'est ce que je propose du haut de ma tête:
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}
Tests:
[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));}
Une solution simple, qui devrait être d'un ordre de grandeur plus rapide qu'une solution regex (basée sur les tests que j'ai exécutés contre les meilleures solutions de ce thread), d'autant plus que la taille de la chaîne d'entrée augmente:
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()
. Et si vous souhaitez diviser sur chaque lettre majuscule, supprimez simplement le plus.Réponses:
J'ai fait ça il y a quelque temps. Il correspond à chaque composant d'un nom CamelCase.
Par exemple:
Pour convertir cela en insérant simplement des espaces entre les mots:
Si vous devez gérer des chiffres:
la source
la source
Excellente réponse, MizardX! Je l'ai légèrement modifié pour traiter les chiffres comme des mots séparés, de sorte que "AddressLine1" devienne "Address Line 1" au lieu de "Address Line1":
la source
Juste pour un peu de variété ... Voici une méthode d'extension qui n'utilise pas de regex.
la source
Mis à part l'excellent commentaire de Grant Wagner:
la source
J'avais besoin d'une solution prenant en charge les acronymes et les nombres. Cette solution basée sur Regex traite les modèles suivants comme des «mots» individuels:
Vous pouvez le faire en une seule ligne:
Une approche plus lisible pourrait être meilleure:
Voici un extrait des tests (XUnit):
la source
Pour plus de variété, en utilisant des objets C # anciens, ce qui suit produit le même résultat que l'excellente expression régulière de @ MizardX.
la source
Voici un prototype qui convertit ce qui suit en cas de titre:
Évidemment, vous n'avez besoin que de la méthode "ToTitleCase" vous-même.
La sortie de la console serait la suivante:
Article de blog référencé
la source
la source
Regex est environ 10 à 12 fois plus lent qu'une simple boucle:
la source
Solution regex naïve. Ne gérera pas O'Conner et ajoute également un espace au début de la chaîne.
la source
Il y a probablement une solution plus élégante, mais c'est ce que je propose du haut de ma tête:
la source
Essayez d'utiliser
Le résultat conviendra au mélange d'alphabet avec des nombres
la source
Implémentation du code psudo à partir de: https://stackoverflow.com/a/5796394/4279201
la source
Pour faire correspondre les catégories Unicode non majuscules et majuscules :
(?<=\P{Lu})(?=\p{Lu})
la source
Implément procédural et rapide:
Tests:
la source
Une solution simple, qui devrait être d'un ordre de grandeur plus rapide qu'une solution regex (basée sur les tests que j'ai exécutés contre les meilleures solutions de ce thread), d'autant plus que la taille de la chaîne d'entrée augmente:
la source