73

In C# is there a way to detect if a string is all caps?

Most of the strings will be short(ie under 100 characters)

StubbornMule
  • 2,720
  • 5
  • 20
  • 19
  • In your loop, once you find a lowercase character then there's no need to continue as it's already failed the test – Steve Kuo Jan 15 '09 at 19:55
  • What do you want the result to be when it sees a non-letter, e.g. punctuation or space? The ToUpper solution returns true; the question returns false. – Jon Skeet Jan 15 '09 at 20:05
  • if(!Char.IsLetter(input[i]) || Char.IsUpper(input[i])) { etc } – Erik Forbes Jan 15 '09 at 20:06
  • Similar to this question: http://stackoverflow.com/questions/234591/upper-vs-lower-case. Is culture going to come in to play? If so the accepted answer isn't correct. – jcollum Jan 15 '09 at 20:07
  • For my purposes I need it to return true when non alpha characters are included. So I need it to be true when it evaluates ABC1 not false. I had tried both solutions and the ToUpper fit my needs better in this instance. – StubbornMule Jan 15 '09 at 21:26
  • Well after looking at the updated answer by Greg Dean it fits my needs even better. It has been ages since I have had to deal with strings. – StubbornMule Jan 16 '09 at 21:38

11 Answers11

115

No need to create a new string:

bool IsAllUpper(string input)
{
    for (int i = 0; i < input.Length; i++)
    {
        if (!Char.IsUpper(input[i]))
             return false;
    }

    return true;
}

Edit: If you want to skip non-alphabetic characters (The OP's original implementation does not, but his/her comments indicate that they might want to) :

   bool IsAllUpper(string input)
    {
        for (int i = 0; i < input.Length; i++)
        {
            if (Char.IsLetter(input[i]) && !Char.IsUpper(input[i]))
                return false;
        }
        return true;
    }
Greg Dean
  • 29,221
  • 14
  • 67
  • 78
  • 1
    This solution is better than the accepted solution (IMHO) because it doesn't need to create an unnecessary string. It may be more lines of code, but that isn't always a bad thing. – Jon Tackabury Jan 15 '09 at 20:05
  • 1
    You win some you lose some. I can't believe how many +1's the accepted answer has received. – Greg Dean Jan 15 '09 at 20:11
  • Simplicity wins over optimization, in the absence of a reason to optimize. – Robert Rossney Jan 15 '09 at 20:42
  • 1
    I agree, this answer is better than anything based on .ToUpper(). – Brian Ensink Jan 15 '09 at 20:56
  • I believe this is a much better solution than the accepted one. – mkchandler Jan 15 '09 at 21:02
  • What would this return on the input "FOO BAR"? – PEZ Jan 15 '09 at 22:28
  • @PEZ false, the same as his original implementation – Greg Dean Jan 16 '09 at 01:07
  • The problem with the question is "I am hoping there is an easier/quicker way to do this." Easier != quicker, if I'm divining what the poster thinks these terms mean. Dean gets my +1. – Jim Nelson Jan 16 '09 at 01:18
  • +1, Nice answer. The accepted answer isn't "bad".. but this one is better imo. – mmcdole Jan 16 '09 at 02:10
  • 2
    Or if (input.All(Char.IsUpper) { } – George Aug 08 '12 at 13:44
  • 2
    I don't see why have a 10-line function when you can have one line asserting a simple regex match... Uncle Googled tells me that something *like* `new Regex(@"[A-Z]+").IsMatch(input)` should work... or... check for [a-z] and there should be no match (if allowing non-letter characters on input) ¯\_(ツ)_/¯ – Joel Quiles Aug 02 '17 at 22:17
99

I like the LINQ approach.

If you want to restrict it to all upper case letters (i.e. no spaces etc):

return input.All(c => char.IsUpper(c));

or using a method group conversion:

return input.All(char.IsUpper);

If you want to just forbid lower case letters:

return !input.Any(c => char.IsLower(c));

or

return !input.Any(char.IsLower);
Jon Skeet
  • 1,421,763
  • 867
  • 9,128
  • 9,194
  • Looks like `c => c.IsUpper(c)` is no longer valid (in VS2013 at least): _error CS0176: Member 'char.IsUpper(char)' cannot be accessed with an instance reference; qualify it with a type name instead_ – joce May 04 '14 at 19:11
  • 1
    @Joce: That would never have been valid - it was a typo. Fixed now, thanks. – Jon Skeet May 04 '14 at 19:15
  • This approach would include non-alphabetic characters in the check. – Paul Nakitare Oct 06 '22 at 11:42
  • 1
    @PaulNakitare: I've given two approaches: one to restrict it to *only* capital letters (nothing else), and one to restrict it to prohibiting lower-case letters. Use whichever option meets the precise requirements. – Jon Skeet Oct 06 '22 at 11:50
65

Simple?

if (input.ToUpper() == input)
{
    // string is all upper
}
BoltBait
  • 11,361
  • 9
  • 58
  • 87
  • 1
    I was all excited about getting an easy best answer, too bad you beat me to it :( – Andrew G. Johnson Jan 15 '09 at 19:55
  • 3
    This evaluates 'true' for strings like "ABC1" or "*!()@". I don't know the original posters context but this solution certainly returns 'true' for strings containing non-capital letters. – Brian Ensink Jan 15 '09 at 20:55
  • 2
    Less code is better code. One way you're unnecessarily converting it to upper, the other way you're unnecessarily converting a simple == into a nine-line looping function. Optimize if profiling shows it's helpful. – Chuck Jan 16 '09 at 08:40
  • 3
    I'd go with ToUpperInvariant() – Dmitri Nesteruk Jan 16 '09 at 10:32
15

Make sure your definition of capitalization matches .Nets definition of capitalization.

ToUpper() in .Net is a linguistic operation. In some languages capitalization rules are not straight forward. Turkish I is famous for this.

// Meaning of ToUpper is linguistic and depends on what locale this executes
// This test could pass or fail in ways that surprise you.
if (input.ToUpper() == input) 
{
    // string is all upper
}

You could use

// Meaning of ToUpper is basically 'ASCII' ToUpper no matter the locale.
if (input.ToUpper(CultureInfo.InvariantCulture) == input) 
{
    // string is all upper
}

You may be tempted to save memory doing character by character capitalization

MSDN cautions against this

for(int i = 0; i < input.Length; i++) {
   if(input[i] != Char.ToUpper(input[i], CultureInfo.InvariantCulture)) {
     return false;
   }
}

The above code introduces a bug. Some non English 'letters' require two .net characters to encode (a surrogate pair). You have to detect these pairs and capitalize them as a single unit.

Also if you omit the culture info to get linguistic capitalization you are introducing a bug where in some locales your home brew capitalization algorithm disagrees with the the .net algorithm for that locale.

Of course none of this matters if your code will never run outside English speaking locales or never receive non English text.

Ifeanyi Echeruo
  • 807
  • 7
  • 7
10

Use

if (input == input.ToUpper())
M4N
  • 94,805
  • 45
  • 217
  • 260
7

I would convert the string to all caps (with ToUpper) then compare that to the original (using Equals). Should be doable in one line of code.

return s.Equals(s.ToUpper())

Nick
  • 13,238
  • 17
  • 64
  • 100
4

If this needs to have good perf, I'm assuming it happens a lot. If so, take your solution and do it a few million times and time it. I suspect what you've got is better than the other solutions because you aren't creating a new garbage collected object that has to be cleaned up, and you can't make a copy of a string without iterating over it anyways.

Joe
  • 2,946
  • 18
  • 17
2

You can also call the Windows function that tells you the makeup of your string.

GetStringTypeEx(LOCALE_USER_DEFAULT, CT_CTYPE1, s, s.Length, ref characterTypes);

You supply it a UInt16 array the same length as your string, and it fills each element with a bitwise combination of flags:

  • C1_UPPER (0x0001): Uppercase
  • C1_LOWER (0x0002): Lowercase
  • C1_DIGIT (0x0004): Decimal digits
  • C1_SPACE (0x0008): Space characters
  • C1_PUNCT (0x0010): Punctuation
  • C1_CNTRL (0x0020): Control characters
  • C1_BLANK (0x0040): Blank characters
  • C1_XDIGIT (0x0080): Hexadecimal digits
  • C1_ALPHA (0x0100): Any linguistic character: alphabetical, syllabary, or ideographic. Some characters can be alpha without being uppercase or lowercase
  • C1_DEFINED (0x0200): A defined character, but not one of the other C1_* types

So if you examined the string:

Hello, "42"!

You would get the results

[0x210, 0x301, 0x382, 0x302, 0x302, 0x302, 0x210, 0x248, 0x210, 0x284, 0x284, 0x210, 0x210]

Which breaks down to:

|            | H | e | l | l | o | , |   | " | 4 | 2 | " | ! |
|------------|---|---|---|---|---|---|---|---|---|---|---|---|
| Defined    | X | X | X | X | X | X | X | X | X | X | X | X |
| Alpha      | x | x | x | x | x |   |   |   |   |   |   |   |
| XDigit     |   | x |   |   |   |   |   |   | x | x |   |   |
| Blank      |   |   |   |   |   |   | x |   |   |   |   |   |
| Cntrl      |   |   |   |   |   |   |   |   |   |   |   |   |
| Punct      |   |   |   |   |   | x |   |   |   |   | x | x |
| Space      |   |   |   |   |   |   | x |   |   |   |   |   |
| Digit      |   |   |   |   |   |   |   |   | x | x |   |   |
| Lower      |   | x | x | x | x |   |   |   |   |   |   |   |
| Upper      | x |   |   |   |   |   |   |   |   |   |   |   |
Ian Boyd
  • 246,734
  • 253
  • 869
  • 1,219
1

Another approach

return input.Equals(input.ToUpper(), StringComparison.Ordinal)
0

I think the following:

bool equals = (String.Compare(input, input.ToUpper(), StringComparison.Ordinal) == 0)

Will work also, and you can make sure that the comparison is made without taking into account the string casing (I think VB.NET ignores case by default). O even use String.CompareOrdinal(input, input.ToUpper()).

Leandro López
  • 2,185
  • 1
  • 15
  • 17
0

Regular expressions comes to mind. Found this out there: http://en.csharp-online.net/Check_if_all_upper_case_string

PEZ
  • 16,821
  • 7
  • 45
  • 66