CamelCase teilen

80

Das ist alles asp.net c #.

Ich habe eine Aufzählung

public enum ControlSelectionType 
{
    NotApplicable = 1,
    SingleSelectRadioButtons = 2,
    SingleSelectDropDownList = 3,
    MultiSelectCheckBox = 4,
    MultiSelectListBox = 5
}

Der numerische Wert davon wird in meiner Datenbank gespeichert. Ich zeige diesen Wert in einem Datagrid an.

<asp:boundcolumn datafield="ControlSelectionTypeId" headertext="Control Type"></asp:boundcolumn>

Die ID bedeutet für einen Benutzer nichts, daher habe ich die gebundene Spalte wie folgt in eine Vorlagenspalte geändert.

<asp:TemplateColumn>
    <ItemTemplate>
        <%# Enum.Parse(typeof(ControlSelectionType), DataBinder.Eval(Container.DataItem, "ControlSelectionTypeId").ToString()).ToString()%>
    </ItemTemplate>
</asp:TemplateColumn>

Das ist viel besser ... Es wäre jedoch großartig, wenn es eine einfache Funktion gäbe, die ich um die Aufzählung legen könnte, um sie nach Camel-Groß- und Kleinschreibung zu teilen, damit die Wörter gut in das Datagrid eingepackt werden.

Hinweis: Mir ist völlig bewusst, dass es dafür bessere Möglichkeiten gibt. Dieser Bildschirm wird ausschließlich intern verwendet und ich möchte nur einen schnellen Hack, um ihn ein wenig besser anzuzeigen.

Robin Day
quelle

Antworten:

76

In der Tat ist ein Regex / Replace der richtige Weg, wie in der anderen Antwort beschrieben. Dies kann jedoch auch für Sie von Nutzen sein, wenn Sie eine andere Richtung einschlagen möchten

    using System.ComponentModel;
    using System.Reflection;

...

    public static string GetDescription(System.Enum value)
    {
        FieldInfo fi = value.GetType().GetField(value.ToString());
        DescriptionAttribute[] attributes = (DescriptionAttribute[])fi.GetCustomAttributes(typeof(DescriptionAttribute), false);
        if (attributes.Length > 0)
            return attributes[0].Description;
        else
            return value.ToString();
    }

Auf diese Weise können Sie Ihre Aufzählungen als definieren

public enum ControlSelectionType 
{
    [Description("Not Applicable")]
    NotApplicable = 1,
    [Description("Single Select Radio Buttons")]
    SingleSelectRadioButtons = 2,
    [Description("Completely Different Display Text")]
    SingleSelectDropDownList = 3,
}

Genommen von

http://www.codeguru.com/forum/archive/index.php/t-412868.html

Eoin Campbell
quelle
+1 Tolle Antwort, ich werde wahrscheinlich die Antwort mit regulären Ausdrücken verwenden, da sie schneller und einfacher ist. Dies ist jedoch eine weitaus bessere Lösung und wird daher akzeptiert.
Robin Day
Ich habe viele Antworten auf Enum-Attribute gesehen, aber das sieht am saubersten aus!
Nawfal
2
klug, aber es ist viel mehr Arbeit als eine einfache statische Regex-Funktion. Ich bin mir nicht sicher, ob ich mit "am saubersten" oder "schneller" oder "einfacher" einverstanden bin. Cleverist? sicher.
Todd Painton
1
Dies funktioniert nur, wenn Sie die Kontrolle über diese Aufzählung haben, aber ich hätte lieber die volle Kontrolle über den Anzeigecode, als davon auszugehen, dass die Aufzählungswerte korrekt geschrieben werden.
Berin Loritsch
131

Ich benutzte:

    public static string SplitCamelCase(string input)
    {
        return System.Text.RegularExpressions.Regex.Replace(input, "([A-Z])", " $1", System.Text.RegularExpressions.RegexOptions.Compiled).Trim();
    }

Entnommen aus http://weblogs.asp.net/jgalloway/archive/2005/09/27/426087.aspx

vb.net:

Public Shared Function SplitCamelCase(ByVal input As String) As String
    Return System.Text.RegularExpressions.Regex.Replace(input, "([A-Z])", " $1", System.Text.RegularExpressions.RegexOptions.Compiled).Trim()
End Function
Tillito
quelle
Einfacher Weg, um den Kamelfallteil zu erreichen ... der andere Ansatz ist besser für mehr Anpassung. Vielen Dank @Tillito
Rolivares
62
Ich habe den regulären Ausdruck ein wenig auf "(? <= [Az]) ([AZ])" geändert. Dies führt dazu, dass ProductID anstelle von Product ID in Product ID konvertiert wird. Es gibt an, dass dem Großbuchstaben ein Kleinbuchstabe vorangestellt werden muss (beachten Sie den Lookbehind-Operator). Außerdem ist die Verkleidung nicht mehr erforderlich.
Ben Mills
7
Hey Ben, warum gibst du das nicht als Antwort? Ein anderer (und ausgefeilterer) Regex ist ein neuer Antwortpartner!
Nicholas Petersen
3
Als Ergänzung zu Bens hilfreichem Kommentar sollte ich erwähnen, dass Sie auch etwas wie "HELLOWorld" mit dem regulären Ausdruck in "HELLO World" aufteilen können: (? <= [AZ]) ([AZ]) (? = [Az] )
Giangurgolo
5
Ich habe die Ausdrücke von Ben Mills und Giangurgolo kombiniert: Regex.Replace (Eingabe, @ "((? <= [AZ]) ([AZ]) (? = [Az])) | ((? <= [Az] +) ([AZ])) ", @" $ 0 ", RegexOptions.Compiled) .Trim ();
IceWarrior353
23

Dieser (^[a-z]+|[A-Z]+(?![a-z])|[A-Z][a-z]+)reguläre Ausdruck kann verwendet werden, um alle Wörter aus dem Namen camelCase oder PascalCase zu extrahieren. Es funktioniert auch mit Abkürzungen an einer beliebigen Stelle im Namen.

  • MyHTTPServerenthält genau 3 Spiele werden: My, HTTP,Server
  • myNewXMLFileenthält 4 Treffer: my, New, XML,File

Sie können sie dann mit zu einer einzigen Zeichenfolge verbinden string.Join.

string name = "myNewUIControl";
string[] words = Regex.Matches(name, "(^[a-z]+|[A-Z]+(?![a-z])|[A-Z][a-z]+)")
    .OfType<Match>()
    .Select(m => m.Value)
    .ToArray();
string result = string.Join(" ", words);
Ghost4Man
quelle
2
Ich mag das. Wir leben jedoch in modernen Zeiten. Daher: @"(^\p{Ll}+|\p{Lu}+(?!\p{Ll})|\p{Lu}\p{Ll}+)"Es ist auch wichtig zu beachten, dass dies überhaupt keine Zahlen ergibt, obwohl sie in Bezeichnern gültig sind.
Daniel B
Einfach und doch perfekt!
JC Raja
1
Ich brauchte eine leichte Änderung zu "(^ [az] + | [AZ] + (?! [Az]) | [AZ] [az] + | [0-9 \. *] + | [Az] +)" "ITPortfolio12v2.0.13BMS" führt zu "IT Portfolio 12 v 2.0.13 BMS" mit jemandem
Joe Johnston
15

Wenn C # 3.0 eine Option ist, können Sie den folgenden Einzeiler verwenden, um die Arbeit zu erledigen:


Regex.Matches(YOUR_ENUM_VALUE_NAME, "[A-Z][a-z]+").OfType<Match>().Select(match => match.Value).Aggregate((acc, b) => acc + " " + b).TrimStart(' ');
em70
quelle
1
Dies behandelt Acroynms im Text nicht wie AMACharter, sondern gibt 'Charter' zurück, nicht 'AMA Charter'.
Adam Mills
Obwohl Änderungen zur Behandlung eines solchen Falls einfach wären (denken Sie daran, etwas wie ([AZ] *) voranzustellen und den Code leicht zu ändern), wird nach meinen Erinnerungen an die Codierungsrichtlinien von Microsoft von der Verwendung solcher All-Caps-Akronyme abgeraten All-Caps-Akronyme Allgemeine Akronyme sollten vermieden werden, wenn sie länger als 2 Buchstaben sind.
Em70
1
Funktioniert bei mir nicht "CamelCase" wird zu "Camel" und nicht zu "Camel Case".
Tillito
15

Tillitos Antwort behandelt keine Zeichenfolgen, die bereits Leerzeichen enthalten, oder Akronyme. Dies behebt es:

public static string SplitCamelCase(string input)
{
    return Regex.Replace(input, "(?<=[a-z])([A-Z])", " $1", RegexOptions.Compiled);
}
Petrucio
quelle
Haftungsausschluss: Tillito, der Anbieter der ursprünglichen Antwort, und Ben Mills, der die Verbesserung in einem Kommentar vorgeschlagen hat, schreiben dies gut. Da es sich um eine verbesserte Antwort handelt und keiner von ihnen sie veröffentlicht oder bearbeitet hat, verdient sie eine separate Antwort. Hätte mir eine halbe Stunde Debugging erspart, wenn es nicht von Anfang an unter Kommentaren begraben worden wäre.
Petrucio
2
Schlägt mit dem einfachen Testfall "SMSMessage" fehl (erwartet: "SMS Message", aktuell: "SMSMessage").
Ian Kemp
10

Hier ist eine Erweiterungsmethode, die Zahlen und mehrere Großbuchstaben korrekt behandelt und auch bestimmte Akronyme in Großbuchstaben in der endgültigen Zeichenfolge berücksichtigt:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Globalization;
using System.Text.RegularExpressions;
using System.Web.Configuration;

namespace System
{
    /// <summary>
    /// Extension methods for the string data type
    /// </summary>
    public static class ConventionBasedFormattingExtensions
    {
        /// <summary>
        /// Turn CamelCaseText into Camel Case Text.
        /// </summary>
        /// <param name="input"></param>
        /// <returns></returns>
        /// <remarks>Use AppSettings["SplitCamelCase_AllCapsWords"] to specify a comma-delimited list of words that should be ALL CAPS after split</remarks>
        /// <example>
        /// wordWordIDWord1WordWORDWord32Word2
        /// Word Word ID Word 1 Word WORD Word 32 Word 2
        /// 
        /// wordWordIDWord1WordWORDWord32WordID2ID
        /// Word Word ID Word 1 Word WORD Word 32 Word ID 2 ID
        /// 
        /// WordWordIDWord1WordWORDWord32Word2Aa
        /// Word Word ID Word 1 Word WORD Word 32 Word 2 Aa
        /// 
        /// wordWordIDWord1WordWORDWord32Word2A
        /// Word Word ID Word 1 Word WORD Word 32 Word 2 A
        /// </example>
        public static string SplitCamelCase(this string input)
        {
            if (input == null) return null;
            if (string.IsNullOrWhiteSpace(input)) return "";

            var separated = input;

            separated = SplitCamelCaseRegex.Replace(separated, @" $1").Trim();

            //Set ALL CAPS words
            if (_SplitCamelCase_AllCapsWords.Any())
                foreach (var word in _SplitCamelCase_AllCapsWords)
                    separated = SplitCamelCase_AllCapsWords_Regexes[word].Replace(separated, word.ToUpper());

            //Capitalize first letter
            var firstChar = separated.First(); //NullOrWhiteSpace handled earlier
            if (char.IsLower(firstChar))
                separated = char.ToUpper(firstChar) + separated.Substring(1);

            return separated;
        }

        private static readonly Regex SplitCamelCaseRegex = new Regex(@"
            (
                (?<=[a-z])[A-Z0-9] (?# lower-to-other boundaries )
                |
                (?<=[0-9])[a-zA-Z] (?# number-to-other boundaries )
                |
                (?<=[A-Z])[0-9] (?# cap-to-number boundaries; handles a specific issue with the next condition )
                |
                (?<=[A-Z])[A-Z](?=[a-z]) (?# handles longer strings of caps like ID or CMS by splitting off the last capital )
            )"
            , RegexOptions.Compiled | RegexOptions.IgnorePatternWhitespace
        );

        private static readonly string[] _SplitCamelCase_AllCapsWords =
            (WebConfigurationManager.AppSettings["SplitCamelCase_AllCapsWords"] ?? "")
                .Split(new[] { ',' }, StringSplitOptions.RemoveEmptyEntries)
                .Select(a => a.ToLowerInvariant().Trim())
                .ToArray()
                ;

        private static Dictionary<string, Regex> _SplitCamelCase_AllCapsWords_Regexes;
        private static Dictionary<string, Regex> SplitCamelCase_AllCapsWords_Regexes
        {
            get
            {
                if (_SplitCamelCase_AllCapsWords_Regexes == null)
                {
                    _SplitCamelCase_AllCapsWords_Regexes = new Dictionary<string,Regex>();
                    foreach(var word in _SplitCamelCase_AllCapsWords)
                        _SplitCamelCase_AllCapsWords_Regexes.Add(word, new Regex(@"\b" + word + @"\b", RegexOptions.Compiled | RegexOptions.IgnoreCase));
                }

                return _SplitCamelCase_AllCapsWords_Regexes;
            }
        }
    }
}
Jerph
quelle
6

Sie können C # -Erweiterungsmethoden verwenden

        public static string SpacesFromCamel(this string value)
        {
            if (value.Length > 0)
            {
                var result = new List<char>();
                char[] array = value.ToCharArray();
                foreach (var item in array)
                {
                    if (char.IsUpper(item) && result.Count > 0)
                    {
                        result.Add(' ');
                    }
                    result.Add(item);
                }

                return new string(result.ToArray());
            }
            return value;
        }

Dann kannst du es gerne benutzen

var result = "TestString".SpacesFromCamel();

Ergebnis wird sein

Testzeichenfolge

Sameera R.
quelle
1
Dies schafft tatsächlich ein Leerzeichen am Anfang, korrigierte den Code
Martin Zikmund
2

Verwenden von LINQ:

var chars = ControlSelectionType.NotApplicable.ToString().SelectMany((x, i) => i > 0 && char.IsUpper(x) ? new char[] { ' ', x } : new char[] { x });

Console.WriteLine(new string(chars.ToArray()));
Andy Rose
quelle
1
Sie sollten zur Codierung mit C \ C ++ zurückkehren: D - viel zu schmutzig für C #
Daten
1
Nun, ich habe gesagt, dass es ein schneller und schmutziger Hack war. Hier ist eine sauberere LINQ-Version.
Andy Rose
Dies behandelt Acroynms im Text nicht wie AMACharter, gibt 'AMA Charter' zurück, nicht 'AMA Charter
Adam Mills
2
public enum ControlSelectionType    
{   
    NotApplicable = 1,   
    SingleSelectRadioButtons = 2,   
    SingleSelectDropDownList = 3,   
    MultiSelectCheckBox = 4,   
    MultiSelectListBox = 5   
} 
public class NameValue
{
    public string Name { get; set; }
    public object Value { get; set; }
}    
public static List<NameValue> EnumToList<T>(bool camelcase)
        {
            var array = (T[])(Enum.GetValues(typeof(T)).Cast<T>()); 
            var array2 = Enum.GetNames(typeof(T)).ToArray<string>(); 
            List<NameValue> lst = null;
            for (int i = 0; i < array.Length; i++)
            {
                if (lst == null)
                    lst = new List<NameValue>();
                string name = "";
                if (camelcase)
                {
                    name = array2[i].CamelCaseFriendly();
                }
                else
                    name = array2[i];
                T value = array[i];
                lst.Add(new NameValue { Name = name, Value = value });
            }
            return lst;
        }
        public static string CamelCaseFriendly(this string pascalCaseString)
        {
            Regex r = new Regex("(?<=[a-z])(?<x>[A-Z])|(?<=.)(?<x>[A-Z])(?=[a-z])");
            return r.Replace(pascalCaseString, " ${x}");
        }

//In  your form 
protected void Button1_Click1(object sender, EventArgs e)
        {
            DropDownList1.DataSource = GeneralClass.EnumToList<ControlSelectionType  >(true); ;
            DropDownList1.DataTextField = "Name";
            DropDownList1.DataValueField = "Value";

            DropDownList1.DataBind();
        }
Kiarash
quelle
2

Die Lösung von Eoin Campbell funktioniert gut, außer wenn Sie einen Webdienst haben.

Sie müssten Folgendes tun, da das Beschreibungsattribut nicht serialisierbar ist.

[DataContract]
public enum ControlSelectionType
{
    [EnumMember(Value = "Not Applicable")]
    NotApplicable = 1,
    [EnumMember(Value = "Single Select Radio Buttons")]
    SingleSelectRadioButtons = 2,
    [EnumMember(Value = "Completely Different Display Text")]
    SingleSelectDropDownList = 3,
}


public static string GetDescriptionFromEnumValue(Enum value)
{
    EnumMemberAttribute attribute = value.GetType()
        .GetField(value.ToString())
        .GetCustomAttributes(typeof(EnumMemberAttribute), false)
        .SingleOrDefault() as EnumMemberAttribute;
    return attribute == null ? value.ToString() : attribute.Value;
}
Theo Koekemoer
quelle
2

Und wenn Sie keine Lust auf Regex haben, versuchen Sie Folgendes:

public static string SeperateByCamelCase(this string text, char splitChar = ' ') {

        var output = new StringBuilder();

        for (int i = 0; i < text.Length; i++)
        {
            var c = text[i];

            //if not the first and the char is upper
            if (i > 0 && char.IsUpper(c)) {

                var wasLastLower = char.IsLower(text[i - 1]);

                if (i + 1 < text.Length) //is there a next
                {
                    var isNextUpper = char.IsUpper(text[i + 1]);

                    if (!isNextUpper) //if next is not upper (start of a word).
                    {
                        output.Append(splitChar);
                    }
                    else if (wasLastLower) //last was lower but i'm upper and my next is an upper (start of an achromin). 'abcdHTTP' 'abcd HTTP'
                    {
                        output.Append(splitChar);
                    }
                }
                else
                {
                    //last letter - if its upper and the last letter was lower 'abcd' to 'abcd A'
                    if (wasLastLower)
                    {
                        output.Append(splitChar);
                    }
                }
            }

            output.Append(c);
        }


        return output.ToString();

    }

Besteht diese Tests, es mag keine Zahlen, aber ich brauchte es nicht.

    [TestMethod()]
    public void ToCamelCaseTest()
    {

        var testData = new string[] { "AAACamel", "AAA", "SplitThisByCamel", "AnA", "doesnothing", "a", "A", "aasdasdAAA" };
        var expectedData = new string[] { "AAA Camel", "AAA", "Split This By Camel", "An A", "doesnothing", "a", "A", "aasdasd AAA" };

        for (int i = 0; i < testData.Length; i++)
        {
            var actual = testData[i].SeperateByCamelCase();
            var expected = expectedData[i];
            Assert.AreEqual(actual, expected);
        }

    }
Hath
quelle
2

Ich habe auch eine, enumdie ich trennen musste. In meinem Fall löste diese Methode das Problem.

string SeparateCamelCase(string str)
{
    for (int i = 1; i < str.Length; i++)
    {
        if (char.IsUpper(str[i]))
        {
            str = str.Insert(i, " ");
            i++;
        }
    }
    return str;
}
Schöner Islam
quelle
1

Einfache Version ähnlich einigen der oben genannten, jedoch mit der Logik, das Trennzeichen nicht automatisch einzufügen (dies ist standardmäßig ein Leerzeichen, kann aber ein beliebiges Zeichen sein), wenn sich bereits eines an der aktuellen Position befindet.

Verwendet eine StringBuildereher als "mutierende" Zeichenfolge.

public static string SeparateCamelCase(this string value, char separator = ' ') {

    var sb = new StringBuilder();
    var lastChar = separator;

    foreach (var currentChar in value) {

        if (char.IsUpper(currentChar) && lastChar != separator)
            sb.Append(separator);

        sb.Append(currentChar);

        lastChar = currentChar;
    }

    return sb.ToString();
}

Beispiel:

Input  : 'ThisIsATest'
Output : 'This Is A Test'

Input  : 'This IsATest'
Output : 'This Is A Test' (Note: Still only one space between 'This' and 'Is')

Input  : 'ThisIsATest' (with separator '_')
Output : 'This_Is_A_Test'
Mark A. Donohoe
quelle
1

#JustSayNoToRegex

Nimmt eine C # -Kennung mit Uderscores und Zahlen und konvertiert sie in eine durch Leerzeichen getrennte Zeichenfolge.

public static class StringExtensions
{
    public static string SplitOnCase(this string identifier)
    {
        if (identifier == null || identifier.Length == 0) return string.Empty;
        var sb = new StringBuilder();

        if (identifier.Length == 1) sb.Append(char.ToUpperInvariant(identifier[0]));

        else if (identifier.Length == 2) sb.Append(char.ToUpperInvariant(identifier[0])).Append(identifier[1]);

        else {
            if (identifier[0] != '_') sb.Append(char.ToUpperInvariant(identifier[0]));
            for (int i = 1; i < identifier.Length; i++) {
                var current = identifier[i];
                var previous = identifier[i - 1];

                if (current == '_' && previous == '_') continue;

                else if (current == '_') {
                    sb.Append(' ');
                }

                else if (char.IsLetter(current) && previous == '_') {
                    sb.Append(char.ToUpperInvariant(current));
                }

                else if (char.IsDigit(current) && char.IsLetter(previous)) {
                    sb.Append(' ').Append(current);
                }

                else if (char.IsLetter(current) && char.IsDigit(previous)) {
                    sb.Append(' ').Append(char.ToUpperInvariant(current));
                }

                else if (char.IsUpper(current) && char.IsLower(previous) 
                    && (i < identifier.Length - 1 && char.IsUpper(identifier[i + 1]) || i == identifier.Length - 1)) {
                        sb.Append(' ').Append(current);
                }

                else if (char.IsUpper(current) && i < identifier.Length - 1 && char.IsLower(identifier[i + 1])) {
                    sb.Append(' ').Append(current);
                }

                else {
                    sb.Append(current);
                }
            }
        }
        return sb.ToString();
    }

}

Tests:

[TestFixture]
static class HelpersTests
{
    [Test]
    public static void Basic()
    {
        Assert.AreEqual("Foo", "foo".SplitOnCase());
        Assert.AreEqual("Foo", "_foo".SplitOnCase());
        Assert.AreEqual("Foo", "__foo".SplitOnCase());
        Assert.AreEqual("Foo", "___foo".SplitOnCase());
        Assert.AreEqual("Foo 2", "foo2".SplitOnCase());
        Assert.AreEqual("Foo 23", "foo23".SplitOnCase());
        Assert.AreEqual("Foo 23 A", "foo23A".SplitOnCase());
        Assert.AreEqual("Foo 23 Ab", "foo23Ab".SplitOnCase());
        Assert.AreEqual("Foo 23 Ab", "foo23_ab".SplitOnCase());
        Assert.AreEqual("Foo 23 Ab", "foo23___ab".SplitOnCase());
        Assert.AreEqual("Foo 23", "foo__23".SplitOnCase());
        Assert.AreEqual("Foo Bar", "Foo_bar".SplitOnCase());
        Assert.AreEqual("Foo Bar", "Foo____bar".SplitOnCase());
        Assert.AreEqual("AAA", "AAA".SplitOnCase());
        Assert.AreEqual("Foo A Aa", "fooAAa".SplitOnCase());
        Assert.AreEqual("Foo AAA", "fooAAA".SplitOnCase());
        Assert.AreEqual("Foo Bar", "FooBar".SplitOnCase());
        Assert.AreEqual("Mn M", "MnM".SplitOnCase());
        Assert.AreEqual("AS", "aS".SplitOnCase());
        Assert.AreEqual("As", "as".SplitOnCase());
        Assert.AreEqual("A", "a".SplitOnCase());
        Assert.AreEqual("_", "_".SplitOnCase());

    }
}
Gru
quelle
0

Versuche dies:

using System;
using System.Linq;
using System.Collections.Generic;

public class Program
{
    public static void Main()
    {
        Console
            .WriteLine(
                SeparateByCamelCase("TestString") == "Test String" // True
            );
    }

    public static string SeparateByCamelCase(string str)
    {
        return String.Join(" ", SplitByCamelCase(str));
    }

    public static IEnumerable<string> SplitByCamelCase(string str) 
    {
        if (str.Length == 0) 
            return new List<string>();

        return 
            new List<string> 
            { 
                Head(str) 
            }
            .Concat(
                SplitByCamelCase(
                    Tail(str)
                )
            );
    }

    public static string Head(string str)
    {
        return new String(
                    str
                        .Take(1)
                        .Concat(
                            str
                                .Skip(1)
                                .TakeWhile(IsLower)
                        )
                        .ToArray()
                );
    }

    public static string Tail(string str)
    {
        return new String(
                    str
                        .Skip(
                            Head(str).Length
                        )
                        .ToArray()
                );
    }

    public static bool IsLower(char ch) 
    {
        return ch >= 'a' && ch <= 'z';
    }
}

Siehe Beispiel online

Kogoia
quelle