Your task for this month’s Exercise is to take a timestamp string and move values from inside a string (characters) into a structure. It’s not an easy exercise, and it doesn’t have a single, best solution.
Here is the sample string, with date and time values buried inside:
"2018_05_15 12:18:22"
Assume that the string retains the same format for any date. So one approach, the first one I tried, would be to calculate the offsets for each value within the string:
year = 0;
month = 5;
year = 8;
hour = 11;
minute = 14;
second = 17;
Every value other than the year
is just two-digits long. You can use these consistent offset values to reference inside the string and extract the two-digit values. That’s what I did for my solution, but I was disappointed because it had a lot of overhead.
For my second approach, I noticed that the string separates each of the values with a single character. The character is either an underscore, space, or a colon, yet these characters are non-digits. Therefore, I can plow the string and replace any non-digit value with a null character, \0
. The result is a buffer that contains tiny little strings, each of a significant value. Here’s how I did that:
while(timestamp[x])
{
if(!isdigit(timestamp[x]))
timestamp[x] = '\0';
x++;
}
The isdigit() function returns TRUE when the character timestamp[x]
is a digit, 0 through 9. The !
(not) negates that condition, meaning the if statement is true for non-digits. If so, the non-digit character at timestamp[x]
is replaced with the null character. The effect is to slice-up the string into smaller chunks, tiny strings of digits inside a character buffer.
Once the string is sliced up, I can use a pointer to stop at each non-null character and translate that mini-string into an integer value.
To march through the buffer, I initialize a pointer variable, offset
, to the base of timestamp[]
:
offset = timestamp;
The first item is the year, which is extracted into the datetime
structure ts
:
ts.year = atoi(offset);
After obtaining that value, a while statement increments the offset
pointer until it encounters a null character:
while(*(offset++));
Because variable offset
is post-incremented, its address references a digit inside the buffer after the while loop stops. At that point, I use the atoi() function to convert the mini-string into an integer value, then store the value in the relevant ts
structure’s member.
Here is the full code:
#include <stdio.h>
#include <stdlib.h>
#include <ctype.h>
int main()
{
char timestamp[] = "2018_05_15 12:18:22";
struct datetime {
int year;
int month;
int day;
int hour;
int minute;
int second;
} ts;
char *offset;
int x = 0;
printf("Time stamp string is '%s'\n",timestamp);
/* replace non-digits with null chars */
while(timestamp[x])
{
if(!isdigit(timestamp[x]))
timestamp[x] = '\0';
x++;
}
offset = timestamp;
ts.year = atoi(offset);
while(*(offset++));
ts.month = atoi(offset);
while(*(offset++));
ts.day = atoi(offset);
while(*(offset++));
ts.hour = atoi(offset);
while(*(offset++));
ts.minute = atoi(offset);
while(*(offset++));
ts.second = atoi(offset);
printf("Timestamp structure is %d %d %d %d %d %d\n",
ts.year,
ts.month,
ts.day,
ts.hour,
ts.minute,
ts.second
);
return(0);
}
This solution proved more elegant than my original effort (which I didn’t save). Still I think it’s clunky: The clutch of statements between Lines 29 and 40 could be replaced with something better. Perhaps you came up with a solution that looks pretty and works properly. If so, great!
You could reduce the code which sets the ts values to a single loop using pointers and pointer arithmetic to access the individual members of ts.
Also, I think atoi stops when it gets to a non-digit so I don’t think replacing non-digits with /0 is necessary.
Slight variation. I typedef’ed the struct.
#include <stdio.h>
#include <stdlib.h>
#include <ctype.h>
int main()
{
char timestamp[] = “2018_05_15 12:18:22”;
typedef struct datetime {
int year;
int month;
int day;
int hour;
int minute;
int second;
} datetime;
printf(“Time stamp string is ‘%s’\n”,timestamp);
datetime dt = {.year = atoi(timestamp),
.month = atoi(timestamp + 6),
.day = atoi(timestamp + 8),
.hour = atoi(timestamp + 11),
.minute = atoi(timestamp + 14),
.second = atoi(timestamp + 17)};
printf(“Timestamp structure is %d %d %d %d %d %d\n”,
dt.year,
dt.month,
dt.day,
dt.hour,
dt.minute,
dt.second
);
return(0);
}
Nice. I like it. And it’s a new trick I’ve not used before. Thanks, Chris!