avinashse
avinashse

Reputation: 1460

Trim function in C

I am writing my own trim() in C. There is a structure which contains all string values, the structure is getting populated from the data coming from a file which contains spaces before and after the beginning of a word.

char *trim(char *string)
{
    int stPos,endPos;
    int len=strlen(string);
    for(stPos=0;stPos<len && string[stPos]==' ';++stPos);
    for(endPos=len-1;endPos>=0 && string[endPos]==' ';--endPos);
    char *trimmedStr = (char*)malloc(len*sizeof(char));
    strncpy(trimmedStr,string+stPos,endPos+1);
    return trimmedStr;
}

int main()
{
    char string1[]="         a sdf ie    ";
    char *string =trim(string1);
    printf("%s",string);    
    return 0;
}

Above code is working fine, but i don't want to declare new variable that stores the trimmed word. As the structure contains around 100 variables.

Is there any way to do somthing like below where I dont need any second variable to print the trimmed string.

printf("%s",trim(string1)); 

I believe above print can create dangling pointer situation.

Also, is there any way where I don't have to charge original string as well, like if I print trim(string) it will print trimmed string and when i print only string, it will print original string

Upvotes: 0

Views: 4031

Answers (6)

Stretchdude
Stretchdude

Reputation: 96

elcuco was faster. but it's done so here we go:

char *trim(char *string)
{
    char *ptr = NULL;
    while (*string == ' ') string++;  // chomp away space at the start
    ptr = string + strlen(string) - 1; // jump to the last char (-1 because '\0')
    while (*ptr == ' '){ *ptr = '\0' ; ptr--; } ; // overwrite with end of string
    return string;  // return pointer to the modified start 
}

If you don't want to alter the original string I'd write a special print instead:

void trim_print(char *string)
{
   char *ptr = NULL;
   while (*string == ' ') string++;  // chomp away space at the start
   ptr = string + strlen(string) - 1; // jump to the last char (-1 because '\0')
   while (*ptr == ' '){ ptr--; } ; // find end of string
   while (string <= ptr) { putchar(*string++); } // you get the picture 
}

something like that.

Upvotes: 1

David C. Rankin
David C. Rankin

Reputation: 84521

If you don't want to change the original, then you will need to make a copy, or pass a second array of sufficient size as a parameter to your function for filling. Otherwise a simple in-place trmming is fine -- so long as the original string is mutable.

An easy way to approach trimming on leading and trailing whitespace is to determine the number of leading whitespace characters to remove. Then simply use memmove to move from the first non-whitespace character back to the beginning of the string (don't forget to move the nul-character with the right portion of the string).

That leaves only removing trailing whitespace. An easy approach there is to loop from the end of the string toward the beginning, overwriting each character of trailing whitespace with a nul-character until your first non-whitespace character denoting the new end of string is found.

A simple implementation for that could be:

#include <stdio.h>
#include <string.h>
#include <ctype.h>

#define DELIM " \t\n"   /* whitespace constant delimiters for strspn */

/** trim leading and trailing whitespace from s, (s must be mutable) */
char *trim (char *s)
{
    size_t  beg = strspn (s, DELIM),    /* no of chars of leading whitespace */
            len = strlen (s);           /* length of s */

    if (beg == len) {   /* string is all whitespace */
        *s = 0;         /* make s the empty-string */
        return s;
    }

    memmove (s, s + beg, len - beg + 1);    /* shift string to beginning */
    for (int i = (int)(len - beg - 1); i >= 0; i--) {   /* loop from end */
        if (isspace(s[i]))  /* checking if char is whitespace */
            s[i] = 0;       /* overwrite with nul-character */
        else
            break;          /* otherwise - done */
    }
    return s;       /* Return s */
}

int main (void) {

    char string1[] = "         a    sdf   ie    ";

    printf ("original: '%s'\n", string1);
    printf ("trimmed : '%s'\n", trim(string1));    
}

(note: additional intervening whitespace was added to your initial string to show that multiple intervening whitespace is left unchanged, the output is single-quoted to show the remaining text boundaries)

Example Use/Output

$ ./bin/strtrim
original: '         a    sdf   ie    '
trimmed : 'a    sdf   ie'

Look things over and let me know if you have further questions.

Upvotes: 0

kiran Biradar
kiran Biradar

Reputation: 12732

Also, is there any way where I don't have to charge original string as well, like if I print trim(string) it will print trimmed string and when i print only string, it will print original string

Yes you can, but you cannot allocate new memory in the trim function as you will not be holding the return memory.

You can have a static char buffer in the trim function and operate on it.

Updated version of @elcuco answer.

#include <stdio.h>

char *trim(char *string)
{
    static char buff[some max length];

    // trim prefix
    while ((*string) == ' ' ) {
         string++;
    }

    // find end of original string

    int i = 0;
    while (*string) {
         buff[i++] = *string;
         string++;
    }

    // trim suffix
    while ((buff[i]) == ' ' ) {
        buff[i] = '\0';
        i--;
    }
    return buff;
}

int main()
{
    char string1[] = "       abcdefg abcdf   ";
    char *string = trim(string1);
    printf("String is [%s]\n",string);
    return 0;
}

With this you don't need to worry about holding reference to trim function return.

Note: Previous values of buff will be overwritten with new call to trim function.

Upvotes: 0

Schwern
Schwern

Reputation: 164639

...is there any way where i don't have to charge original string as well, like if i do trim(string) it will print trimmed string and when i print only string, it will print original stringavinashse 8 mins ago

Yes, though it gets silly.

You could modify the original string.

trim(string);
printf("trimmed: %s\n", string);

The advantage is you have the option of duplicating the string if you want to retain the original.

char *original = strdup(string);
trim(string);
printf("trimmed: %s\n", string);

If you don't want to modify the original string, that means you need to allocate memory for the modified string. That memory then must be freed. That means a new variable to hold the pointer so you can free it.

char *trimmed = trim(original);
printf("trimmed: %s\n", trimmed);
free(trimmed);

You can get around this by passing a function pointer into trim and having trim manage all the memory for you.

#include <string.h>
#include <stdlib.h>
#include <stdio.h>

void trim(char *string, void(*func)(char *) )
{
    // Advance the pointer to the first non-space char
    while( *string == ' ' ) {
        string++;
    }

    // Shrink the length to the last non-space char.
    size_t len = strlen(string);
    while(string[len-1]==' ') {
        len--;
    }

    // Copy the string to stack memory
    char trimmedStr[len + 1];
    strncpy(trimmedStr,string, len);

    // strncpy does not add a null byte, add it ourselves.
    trimmedStr[len] = '\0';

    // pass the trimmed string into the user function.
    func(trimmedStr);
}

void print_string(char *str) {
    printf("'%s'\n", str);
}

int main()
{
    char string[]="         a sdf ie    ";
    trim(string, print_string);
    printf("original: '%s'\n", string);
    return 0;
}

Ta da! One variable, the original is left unmodified, no memory leaks.

While function pointers have their uses, this is a bit silly.

It's C. Get used to managing memory. ¯\_(ツ)_/¯

Upvotes: 0

Rishikesh Raje
Rishikesh Raje

Reputation: 8614

You can modify the function by giving the output in the same input string

void trim(char *string)
{
    int i;
    int stPos,endPos;
    int len=strlen(string);
    for(stPos=0;stPos<len && string[stPos]==' ';++stPos);
    for(endPos=len-1;endPos>=0 && string[endPos]==' ';--endPos);
    for (i=0; i<=(endPos-stPos); i++)
    {
        string[i] = string[i+stPos];
    }
    string[i] = '\0'; // terminate the string and discard the remaining spaces.
}

Upvotes: 0

elcuco
elcuco

Reputation: 9208

You could the original string in order to do this. For trimming the prefix I just advance the pointer, and for the suffix, I actually add \0. If you want to keep the original starting as is, you will have to move memory (which makes this an O(n^2) time complexity solution, from an O(n) I provided).

#include <stdio.h>

char *trim(char *string)
{
    // trim prefix
    while ((*string) == ' ' ) {
         string ++;
    }

    // find end of original string
    char *c = string;
    while (*c) {
         c ++;
    }
    c--;

    // trim suffix
    while ((*c) == ' ' ) {
        *c = '\0';
        c--;
    }
    return string;
}

int main()
{
    char string1[] = "       abcdefg abcdf   ";
    char *string = trim(string1);
    printf("String is [%s]\n",string);
    return 0;
}

(re-thinking... is it really O(n^2)? Or is it O(2n) which is a higher O(n)...? I guess depending on implementation)

Upvotes: 0

Related Questions