I'm looking to remove all punctuation from a string and make all uppercase letters lower case in C, any suggestions?
-
1Do you need to do it in-place or can you work in a new buffer? – jason Dec 03 '09 at 18:08
-
Jason, eliben's answer would work fine when the source==destination ;-) – Michael Krelin - hacker Dec 03 '09 at 18:09
-
2Are you doing this in English? Some languages have problems with lowercasing characters. – David Thornley Dec 03 '09 at 18:14
-
sounds a lot like homework... – Dima Dec 03 '09 at 22:43
4 Answers
Just a sketch of an algorithm using functions provided by ctype.h
:
#include <ctype.h>
void remove_punct_and_make_lower_case(char *p)
{
char *src = p, *dst = p;
while (*src)
{
if (ispunct((unsigned char)*src))
{
/* Skip this character */
src++;
}
else if (isupper((unsigned char)*src))
{
/* Make it lowercase */
*dst++ = tolower((unsigned char)*src);
src++;
}
else if (src == dst)
{
/* Increment both pointers without copying */
src++;
dst++;
}
else
{
/* Copy character */
*dst++ = *src++;
}
}
*dst = 0;
}
Standard caveats apply: Completely untested; refinements and optimizations left as exercise to the reader.

- 39,039
- 2
- 53
- 68
-
2
-
1You should cast the argument of the `is*` or `to*` functions to `unsigned char`. That is not a refinement or optimization! – pmg Dec 03 '09 at 18:14
-
@pmg Or I could say this is restricted to ASCII strings. Like I say, it's a sketch of an algorithm. :-) At any rate, I was going to update it but it looks like Sinan beat me to it. Thanks guys. – asveikau Dec 03 '09 at 18:18
-
1Since tolower() is usually implemented as a macro, you want to take the post-increment operator out of there, otherwise you'll have some nasty side-effects. – Ferruccio Dec 03 '09 at 20:32
-
The second else if clause should be: else if (*src == *dst) But you could actually take it out completely and let the final else just copy matching characters. – Ferruccio Dec 03 '09 at 20:35
-
@Ferruccio Very good point about the macro. I remember reading that in a manpage for something in ctype, once, actually. Fixed. As for the last else if... it's kind of a micro-optimization anyway. My original solution copied matching characters. – asveikau Dec 03 '09 at 21:16
-
@asveikau - yes, but the else clause is comparing the pointers, not the characters they point to. – Ferruccio Dec 03 '09 at 21:53
-
@Ferruccio yes, I was not thinking about it from the point of view of equal characters at different addresses. I was thinking about the fact that if the addresses are equal you don't need to to "move" characters. at any rate the whole thing is a bit of a micro-optimization, though my guess is comparing pointers is faster than dereferencing bytes and comparing the values. :P – asveikau Dec 03 '09 at 23:09
-
Loop over the characters of the string. Whenever you meet a punctuation (ispunct
), don't copy it to the output string. Whenever you meet an "alpha char" (isalpha
), use tolower
to convert it to lowercase.
All the mentioned functions are defined in <ctype.h>
You can either do it in-place (by keeping separate write pointers and read pointers to the string), or create a new string from it. But this entirely depends on your application.

- 263,248
- 89
- 350
- 412
-
more detail here: http://stackoverflow.com/questions/421616/best-way-to-strip-punctuation-from-a-string – TStamper Dec 03 '09 at 18:08
-
TStamper, there seems to be no C example there! C#, C++, but no C – Eli Bendersky Dec 03 '09 at 18:10
-
@eliben- I was meaning in detail as for examples, not language specific – TStamper Dec 03 '09 at 18:12
-
By the time I wrote full compilable program and tested it, there was already another answer doing basically the same thing. So, I deleted mine and upvoted @asveikau's answer. – Sinan Ünür Dec 03 '09 at 18:19
-
The idiomatic way to do this in C is to have two pointers, a source and a destination, and to process each character individually: e.g.
#include <ctype.h>
void reformat_string(char *src, char *dst) {
for (; *src; ++src)
if (!ispunct((unsigned char) *src))
*dst++ = tolower((unsigned char) *src);
*dst = 0;
}
src and dst can be the same string since the destination will never be larger than the source.
Although it's tempting, avoid calling tolower(*src++)
since tolower may be implemented as a macro.
Avoid solutions that search for characters to replace (using strchr or similar), they will turn a linear algorithm into a geometric one.

- 98,941
- 38
- 226
- 299
-
-
The argument to the `is*` and `to*` function should be cast to `unsigned char`. – pmg Dec 03 '09 at 18:22
-
-
Searching for characters to replace will not make the algorithm exponential. Perhaps you are thinking it will change O(n) to O(n^2). This would be a geometric algorithm, not exponential (O(2^n)). But unless the characters to be replaced depends on the input in some way, the searching version will only multiply the algorithm's time by some constant (the number of such characters), which is still O(n) (though, obviously, a much less efficient O(n)). – Jeffrey L Whitledge Dec 03 '09 at 18:56
-
-
I seem to recall seeing weird behavior if you call tolower(c) and isupper(c) returns false. So I usually shield to*() calls with is*() first. – asveikau Dec 03 '09 at 21:18
-
@asveikau - you may be thinking of _tolower(), which requires that it is passed an upper-case character. tolower() is supposed to check for that. – Ferruccio Dec 03 '09 at 21:49
-
@Ferruccio Nope, I'm pretty sure this was tolower() (no underscore) with glibc. I wasn't aware that an underscored version exists. – asveikau Dec 03 '09 at 23:17
Here's a rough cut of an answer for you:
void strip_punct(char * str) {
int i = 0;
int p = 0;
int len = strlen(str);
for (i = 0; i < len; i++) {
if (! ispunct(str[i]) {
str[p] = tolower(str[i]);
p++;
}
}
}

- 17,546
- 23
- 105
- 172
-
1See *Shlemiel the painter's algorithm*: http://www.joelonsoftware.com/articles/fog0000000319.html – Sinan Ünür Dec 03 '09 at 18:20
-
The argument to the `is*` and `to*` function should be cast to `unsigned char`. – pmg Dec 03 '09 at 18:22
-
Shlemiel does not apply here: the `strlen()` function is used once outside the loop. – pmg Dec 03 '09 at 18:26
-
1@pmg It is not strictly Shlemiel but the function does traverse the string twice: Once to find the length and then to transform. – Sinan Ünür Dec 03 '09 at 18:51
-
1