1

Hey there! I'm stuck on an ANSI C problem which I think should be pretty trivial (it is at least in any modern language :/).

The (temporary) goal of my script is to split a string (array of char) of 6 characters ("123:45") which represents a timestamp minutes:seconds (for audio files so it's ok to have 120 minutes) into just the minutes and just the seconds.

I tried several approaches - a general one with looking for the ":" and a hardcoded one just splitting the string by indices but none seem to work.

void _splitstr ( char *instr, int index, char *outstr ) {
char temp[3]; 
int i; 
int strl = strlen ( instr );
if ( index == 0 ) {
    for ( i = 0; i < 3; ++i ) {
        if ( temp[i] != '\0' ) {
            temp[i] = instr[i];
        }
    }
} else if ( index == 1 ) {
    for ( i = 6; i > 3; i-- ) {
            temp[i] = instr[i];
        }
    }
strcpy ( outstr, temp );
}

Another "funny" thing is that the string length of an char[3] is 6 or 9 and never actually 3. What's wrong with that?

Thomas Dickey
  • 51,086
  • 7
  • 70
  • 105
Asmodiel
  • 1,002
  • 1
  • 12
  • 21
  • 1
    homework? no problem if it is, just let us know – pmg Apr 20 '11 at 16:11
  • 1
    Your code tries to put 3 characters into `temp` (which it can hold) and then use `temp` in a "string" function. A string needs to be terminated by a null byte. Your `temp` does not have space for the null byte. – pmg Apr 20 '11 at 16:13

7 Answers7

6

How about using sscanf(). As simple as it can get.

    char time[] = "123:45";
    int minutes, seconds;

    sscanf(time, "%d:%d", &minutes, &seconds);

This works best if you can be sure that time string syntax is always valid. Otherwise you must add check for that. On success, sscanf function returns the number of items succesfully read so it's pretty easy to detect errors too.

Working example: http://ideone.com/vVoBI

Athabaska Dick
  • 3,855
  • 3
  • 20
  • 22
  • 1
    God this could have spared me a whole day of frantic debugging, thanks! I didn't think it would be that easy >.> – Asmodiel Apr 20 '11 at 16:28
2

How about...

int seconds, minutes;
minutes = atoi(instr);
while(*instr != ':' && *++instr != '\0');
seconds = atoi(instr);

Should be pretty fast.

Wolph
  • 78,177
  • 11
  • 137
  • 148
  • Although I think this can crash with a SIGSEGV or similar (or possibly produce nonsense results) if `instr` happens not to contain a colon character, which is a little harsh. – Simon Nickerson Apr 20 '11 at 16:18
  • @Simon Nickerson, @Athabaska Dick: indeed. it's not forgiving for errors. I'll add the `NULL` check. Slightly slower but shouldn't be a problem ;) – Wolph Apr 20 '11 at 22:12
1

you can try something like that:

void break_string(const char* input, char* hours, char* minutes)
{
    if(input == 0 || hours == 0 || minutes == 0)
        return;

    while(*input != ':')
        *hours++ = *input++;

    *hours = '\0';
    ++input;

    while(*minutes++ = *input++);

    return;
}

Here is the same function a bit simplified:

void break_string(const char* input, char* hours, char* minutes)
{
    if(input == 0 || hours == 0 || minutes == 0)
        return;

    while(*input != ':')
    {
        *hours = *input;
        ++hours;
        ++input;
    }
    *hours = '\0';

    ++input; //ignore the ':' part
    while(*input)
    {
        *minutes = *input;
        ++minutes;
        ++input;
    }
    *minutes = '\0';

    return;
}

int main()
{
    char* test = "123:45";
    char* minutes   = malloc( sizeof(char) * 12 );
    char* hours     = malloc( sizeof(char) * 12 );
    break_string( test , hours , minutes );
    printf("%s , %s \n" , hours , minutes ); 
    //...
    free( minutes );
    free( hours ) ;
}
1

You have basically three options

  • change the input string (can't be a string literal)
  • copy data to output strings (input can be a literal)
  • transform sequences of characters to numbers

Changing the input string implies transforming "123:45" to "123\0" "45" with an embedded null.

Copying data implies managing storage for the copy.

Transforming sequences of characters implies using, for example, strtol.

pmg
  • 106,608
  • 13
  • 126
  • 198
1

You aren't putting a terminating null on your string in temp[], so when you do a strlen(temp), you are accessing arbitrary memory.

Using your known lengths, you can use something like this:

char temp[4];
if (index==0)
{
  strncpy(temp, instr, 3);
  temp[3] = 0;
}
else if (index==1)
{
  strncpy(temp, instr+4, 2);
  temp[2] = 0;
}
strcpy(outstr, temp);

But, I'll caution that I've skipped all sorts of checking for valid lengths in instr and outstr.

garlon4
  • 1,162
  • 10
  • 14
1

This?

char *mins, *secs;
mins = str;
while(*(++str) != ':');
str[0] = '\0';
secs = s + 1;
Rumple Stiltskin
  • 9,597
  • 1
  • 20
  • 25
1

Here's one way, I have ignore the "index" argument above:

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

void _splitstr ( char *instr, char *outstr ) {
        char temp[10];
        char* end = strchr(instr, ':');
        int i = 0;

        if(end != 0) {
                while(instr != end)
                        temp[i++] = *instr++;
                temp[i] = '\0';
                strcpy(outstr, temp);
        } else {
                outstr = '\0';
        }
}
Shyamal Pandya
  • 532
  • 3
  • 16