tags:

views:

138

answers:

6

I have a string representing an integer with spaces -- digits are grouped by three.

I was considering using strchr and strcat, as in:

char* remove_spaces (char* s)
{
    char* space;
    while (space = strchr(s, ' '))
    {
        *space = '\0';
        strcat(s, space + 1);
    }

    return s;
}

But, first, I'm not sure it is safe to use strcat this way since the string to be appended overlaps the final string.

Next, I'm wondering whether this could be done better with something like sscanf.

+2  A: 
char* trim (char* s)
{
    char* space;
    while (space = strchr(s, ' '))
    {
        memmove(space,space+1,strlen(space));
    }

    return s;
}
Isn't this going to be really inefficient, due to repeatedly reading and writing later segments of the string?
David Given
performance was not the question
A: 

No, your use of strcat is not safe (§7.21.3.1/2: "If copying takes place between objects that overlap, the behavior is undefined.")

If you do a bit of looking, you can probably find a few dozen (or more) implementations of this on the web (one example).

Jerry Coffin
+1  A: 

You could use strtok

//asuming line points to the beginning of your string

char *col_str = line, c;
short int *the_numbers;
int col, col_num, count = 0;
while((c = *col_str++) != '\0'){
    if(c == ' '){
        count++;
    }
}

the_numbers = (*short int)malloc(sizeof(short int)*count+1);

for(col_num = 0,col_str = line; ; col_num++,col_str = NULL){
    col = atoi(strtok(col_str, ' '));
    the_numbers[col_num] = (short int)col;
}

EDIT:

If you have a constant number of items in each line you could just use malloc with that value instead of pre-counting the number of spaces in the string.

short int *the_numbers = (short int*)malloc(NUM_ITEMS * sizeof(short int));

You could probably do this with malloc and realloc as well but I'm not sure if that would be faster.

GWW
This was what i was going to suggest.
James
I don't think this does what the author was asking about --- isn't col going to end up containing the numeric value of the last clause of the number only?
David Given
He would have to store them in an integer array, I suppose I should include that part
GWW
+1  A: 

For this kind of simple problem it's usually easiest just to loop through character by character:

void trim(char* buffer)
{
    char* r = buffer;
    char* w = buffer;
    for (;;)
    {
        char c = *r++;
        if (c != ' ')
            *w++ = c;
        if (c == '\0')
            break;
    }
}

It's safe to use the same buffer for both reading and writing because we know the trimmed string will always be shorter than the original string. This is the fastest possible solution as each character is read once and written at most once.

You can't use strcpy() when the source and destination overlap --- the specification forbids it.

I'm don't know about scanf(); there's all kinds of obscure yet useful stuff buried deep within it, and it's worth going through the man page.

Edited: fixed the stupid typo that meant it didn't work.

David Given
-1 This doesn't actually change the contents of buffer
Patrick
D'oh! You're right. Fixed.
David Given
-1 removed and had just added my own version of your idea
Patrick
+1  A: 

An alternative method based on David Given's:

void removeSpaces( char* str )
{
    char* input = str;
    char* output = str;
    for( ; *input != 0; ++input )
    {
        if( *input != ' ' )
            *output++ = *input;
    }
    *output = 0;
}

I wouldn't worry about performance issues of using memmove unless your strings are really large. There isn't an easy way of using sscanf for this as it is hard to define where in the input string each call to sscanf should begin.

Patrick
I accepted gordongekko's answer since strings are short enough not to impact performance, but I really love yours !
paulo.arras
A: 

You could use strtoul for the conversion, without having to manipulate the string at all. strtoul converts as much as it can, and tells you where it stopped. Handily it also skips over leading white space. So:

static  unsigned long   conv( const char* s)
{   unsigned long   num, dig;
    char* endp;

    for(num=0;;s=endp)
    {      dig = strtoul( s, &endp, 10);
            if ( s == endp)
            {   break;
            }
            num = num*1000 + dig;
    }
    return num;
}
dmuir