How can I convert a mixed case string to a lowercase string in C?
Asked
Active
Viewed 3.5e+01k times
140
-
2Are you just dealing with ASCII with letters a-z only? – Mark Byers Apr 18 '10 at 09:52
-
1ascii. how would i take that into account? would the example below still work? what happens if my char is a '#' and tolower() gets called on it? – Tony Stark Apr 18 '10 at 10:10
-
1That will work. I was more thinking if your string contains things like é or Ü. – Mark Byers Apr 18 '10 at 10:46
-
4Why not just use "strlwr"? `strlwr((char*)str);` It just goes through the string and converts it itself. – Larry Mar 01 '18 at 23:49
-
1@Larry It's non-standard. – mid May 28 '18 at 20:45
6 Answers
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
-
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
-
1this 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
-
4I believe the one liner will cause you to lose your pointer to the string. – Ace.C Sep 08 '17 at 19:55
-
2
-
-
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
-
1The 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
-
7To 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
-
8This 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
-
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.
-
6Yeah, 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
-
1This 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
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
-