140

How can I convert a mixed case string to a lowercase string in C?

Evan Carroll
  • 78,363
  • 46
  • 261
  • 468
Tony Stark
  • 24,588
  • 41
  • 96
  • 113

6 Answers6

206

It's in the standard library, and that's the most straight forward way I can see to implement such a function. So yes, just loop through the string and convert each character to lowercase.

Something trivial like this:

#include <ctype.h>

for(int i = 0; str[i]; i++){
  str[i] = tolower(str[i]);
}

or if you prefer one liners, then you can use this one by J.F. Sebastian:

for ( ; *p; ++p) *p = tolower(*p);
Evan Carroll
  • 78,363
  • 46
  • 261
  • 468
Earlz
  • 62,085
  • 98
  • 303
  • 499
  • 41
    `for ( ; *p; ++p) *p = tolower(*p);` seems more idiomatic. – jfs Apr 18 '10 at 09:58
  • 15
    @J.F. there you go. Depends on if they want the code to look scary or nice :) (very readable one liner, but it does look scary) – Earlz Apr 18 '10 at 10:05
  • 1
    this gives me a segfault if str is a `char *`, but not if str is a char array. Got any explanation for that? – Electric Coffee Nov 22 '16 at 22:07
  • @ElectricCoffee I guess if you used `char *` you used really a string constant which gets placed in unwriteable memory section. So if you try to change the value of that it gives you a segfault. If, on the other hand, you would copy that constant string to a `char *copy = strdup (const_string)` then `copy` could be altered because it is allocated on the heap, which is writeable. So all in all it does not have anything to do with using a pointer (`char *`) but with using constant strings like in code: `char *const_str = "my const string"`. – Jan Sep 05 '17 at 11:42
  • 4
    I believe the one liner will cause you to lose your pointer to the string. – Ace.C Sep 08 '17 at 19:55
  • 2
    I believe that one liner will have untold ramifications. – NOP da CALL Mar 25 '18 at 01:12
  • the one-liner results in `error: lvalue required as increment operand` – nmz787 Apr 25 '18 at 21:00
  • 1
    @jfs Why not `while (*p = tolower(*p)) ++p;` or `while (*p = tolower(*p++))` for the purpose of obfuscation (the latter only works since C++17) ;-) – L. F. May 18 '19 at 07:46
  • 1
    The last character in the c string represented by **p** is the character ``'\0'`` numericaly represented as literal ``0``. So when it encounters the last character as it increments, the loop terminates since it is falsy. I believe there are no ramifications [NOP da CALL](https://stackoverflow.com/users/5352244/nop-da-call). – thetva Nov 01 '22 at 18:28
9

to convert to lower case is equivalent to rise bit 0x60 if you restrict yourself to ASCII:

for(char *p = pstr; *p; ++p)
    *p = *p > 0x40 && *p < 0x5b ? *p | 0x60 : *p;
Deduplicator
  • 44,692
  • 7
  • 66
  • 118
Oleg Razgulyaev
  • 5,757
  • 4
  • 28
  • 28
  • 7
    To make it slightly more readable you could do `for(char *p = pstr;*p;++p) *p=*p>='A'&&*p<='Z'?*p|0x60:*p;` – Grant Peters Apr 18 '10 at 10:54
  • 8
    This version is actually slower than glibc's `tolower()`. 55.2 vs. 44.15 on my machine. – jfs Apr 18 '10 at 18:10
  • i can't imagine that: tolower() deals with chars; only if it's macro – Oleg Razgulyaev Apr 18 '10 at 18:37
  • 1
    @oraz: tolower() has `int (*)(int)` signature. Here's the code used for performance measurements http://gist.github.com/370497 – jfs Apr 18 '10 at 19:32
  • @J.F.: i see, they've used table, but i can optimize: for ( ; *p; ++p) if(*p > 'Z') {continue;} else if (*p < 'A') {continue;} else {*p = *p|0x60;} – Oleg Razgulyaev Apr 18 '10 at 20:27
  • @oraz: `if (*p > 'Z')` optimization performs better on the input I've used, but if there are many upper-case letters it takes the same time as the previous version. – jfs Apr 18 '10 at 21:33
  • 1
    The fastest version uses a lookup table instead of branches. – Joe May 08 '20 at 23:24
  • Note that this is an ASCII-only conversion that is not strictly conforming C. C provides no guarantees that upper- (or lower-)case letter are represented consecutively in any character set. – Andrew Henle Jul 02 '20 at 23:25
6

Looping the pointer to gain better performance:

#include <ctype.h>

char* toLower(char* s) {
  for(char *p=s; *p; p++) *p=tolower(*p);
  return s;
}
char* toUpper(char* s) {
  for(char *p=s; *p; p++) *p=toupper(*p);
  return s;
}
cscan
  • 364
  • 2
  • 9
  • Well if you're going the one-liner way, then `s` is a local variable in your function, you can directly use it instead of declaring `p`.` – NewbiZ Mar 08 '22 at 02:32
  • @NewbiZ, indeed: // convert string to lowercase, in place: char* toLower(char* p) { for( ; *p; p++) *p=tolower(*p); return p; } – Felipe G. Nievinski Jun 26 '23 at 04:18
1

If we're going to be as sloppy as to use tolower(), do this:

char blah[] = "blah blah Blah BLAH blAH\0";
int i = 0;
while( blah[i] |=' ', blah[++i] ) {}

But, well, it kinda explodes if you feed it some symbols/numerals, and in general it's evil. Good interview question, though.

Aubin
  • 14,617
  • 9
  • 61
  • 84
Ken S
  • 35
  • 1
  • 6
    Yeah, this will fold/spindle/mutilate a variety of symbols (in ASCII, any symbol, control character, or numeral with bit 5 clear will become the same character code with bit 5 set, etc) so really, seriously, don't use it. – Ken S May 22 '13 at 21:26
  • 1
    This post is discussed on [meta](http://meta.stackoverflow.com/questions/270402/is-ghetto-an-offensive-word). – Patrick Hofman Sep 02 '14 at 08:31
  • Can you elaborate more? When I read about tolower(), they all mention that they only work on characters that have a lowercase character defined for them. From opengroup.org: "If the argument of tolower() represents an uppercase letter, and there exists a corresponding lowercase letter [CX] [Option Start] (as defined by character type information in the program locale category LC_CTYPE ), [Option End] the result shall be the corresponding lowercase letter. All other arguments in the domain are returned unchanged." If this is the case, where does tolower() fail? – 9a3eedi Mar 21 '22 at 11:20
1

If you need Unicode support in the lower case function see this question: Light C Unicode Library

Community
  • 1
  • 1
Eduardo
  • 2,327
  • 5
  • 26
  • 43
-1

Are you just dealing with ASCII strings, and have no locale issues? Then yes, that would be a good way to do it.

Mark Byers
  • 811,555
  • 193
  • 1,581
  • 1,452
  • what happens if tolower() is called on a non-ascii a-z char? like '!' or '#'. i tested it on '#' and it seemed to work ok. is this generally true for all ascii chars that aren't letters a-z? – Tony Stark Apr 18 '10 at 10:29
  • 1
    @hatorade: `tolower()` leaves argument unchanged if it is not in 'A'..'Z' range. – jfs Apr 18 '10 at 18:20
  • 2
    ! and # are both ascii chars. Mark was referring to other encodings like UTF8, where you can't assume that there is one byte per character (as this solution does) – hdgarrood Nov 23 '12 at 12:31
  • This should be a comment not an answer. – Bryan Green Jun 29 '23 at 20:40