Reputation: 29
I have a text file as input that has an unknown number of lines and a semi-unknown format. An example would be:
p1 a x1 300
p2 b x3 200 c x2 x1 500
p3 c x1 x3 1500 b x2 100 a x3 800
Where the 'c' operations involve two 'x' objects and the others involve one. The order of the operations can be random, and the amount of operations is also random. I need to store the 'p' number, operation type, associated 'x' object(s) and the integer at the end of the operation.
I don't know how to go about storing this data as scanf()
only works with formatted input that is known. I've tried to traverse each line character-by-character using getc()
but I can't detect the end of a file this way while also trying to grab and check multiple characters.
If anyone could help me figure out a way to store this data I would appreciate it.
EDIT:
My text file has other lines, too, but the final portion is the one I've specified. Currently this only grabs the first operation in the line 'n' times, where 'n' is the total number of operations in the line.
for (i = 0; i < lines; i++) {
if(fgets(line, MAXCHAR, fp)) {
if (line[0] == 'p' && isdigit(line[1])) { // Check if we are interested in parsing the line
k = opCount(line); // Amount of operations in line - Working
data[c_count].t_num = k;
data[c_count].trans = calloc(data[c_count].t_num, sizeof(clients));
for (j = 0; j < k; j++) {
tempBuf = strdup(line);
if(tempBuf) {
token = strtok(tempBuf, delim);
if(token) {
//1st of n sections = p1 a x1 300
token = strtok(NULL, " ");//parsing on space now
if(token) {
strcpy(data[c_count].trans[j].lineNum, token);
//2nd section is opType
token = strtok(NULL, " ");
if (token) {
data[c_count].trans[j].op_type = atoi(token);
token = strtok(NULL, " ");
if (token) {
data[c_count].trans[j].val = atoi(token);
}
}
}
}
}
free(tempBuf);
}
c_count++;
}
}
I'm also not sure this accounts for the 'c' case, where two objects are involved...
Upvotes: 0
Views: 98
Reputation: 23218
...don't know how to go about storing this data as scanf()....
From what you have described in your post, the syntax of your input is actually known well enough to parse it. Here are some suggestions, and some pseudo code to get you started:
Note: scanf is not the best choice for parsing strings, even if there is some predictability to their syntax. I suggest learning to use fgets in conjunction with strtok (or strtok_r) as an alternative.
const char filespec[] = {"C:\\dev\\play\\data.txt"};
typedef struct { //container for operation/line
char lineNum[10];
char opType;
char op[2];
int val; //you did not describe this in question
}OP;
typedef struct { //container for each line
int opsCountOnLine;
OP *section;
}LINE;
const char *subs[] = {" a ", " b ", " c "};
int getLineCount(const char *filespec);//Counts '\n'. Adjust function if other criteria
int countOfOpsPerLine(const char line);//Counts occurances of 'a', 'b', 'c', ... on a line
int main(void)
{
int lineCount = getLineCount(filespec);
int i;
char line[280]; //adjust as necessary
FILE *fp = NULL;
LINE *lines = calloc(lineCount, sizeof(*lines));
if(lines)
{
fp = fopen(filespec, "r");
if(fp)
{
for(i=0;i<lineCount;i++)
{
if(fgets(line, 280, fp))
{
//here is where LINE can be populated with content of each line
//1. determine operations in 'line'.
lines[i].opsCountOnLine = countOfOpsPerLine(line);
//2. allocate memory for sections.
lines[i].section = calloc(lines[i].opsCountOnLine, sizeof(OP));
for(j=0;j<lines[i].opsCountOnLine;j++)
{
tempBuf = strdup(line);
if(tempBuf)
{
//SEE EDIT below for suggested content in this inner loop.
//3. Parse line into lines[i].section[0].line, lines[i].section[0].opType, etc.
// using delimiter of "abc" (...def...) with strtok()
//4. Other steps???
//then free tempBuf so you can use it again in the loop
free(tempBuf);
}
}
else
{
//notify user of error, exit
free(lines);
}
}
fclose(fp);
}
}
return 0;
}
int countOfOpsPerLine(const char *line) //Counts occurances of 'a', 'b', 'c', ... on a line
{
int count=0, i;
char *tok;
// Parse line to find number of sections
for(i=0;i<sizeof(subs)/sizeof(subs[0]);i++)
{
tok = strstr(line,subs[i]);
if(tok != NULL) count++;
}
return count;
}
int getLineCount(const char *filespec)
{
int c=0, count=0;
FILE *fp = fopen(filespec, "r");
if(fp)
{
for (c = getc(fp); c != EOF; c = getc(fp))
{
if (c == '\n') // Increment count if this character is newline
{
count = count + 1;
}
}
fclose(fp);
}
return count;
}
EDIT to include suggestion for inner loop (and edited main struct above) Since your input text seems to use a or b or c for opType, you can use them as the delimiteror... you can use
const char delim[] = {"abc"};
char *tok;
tok = strtok(tempBuf, delim);
if(tok)
{
//1st of n sections = p1 a x1 300
tok = strtok(NULL, " ");//parsing on space now
{
if(tok)
{
strcpy(lines[i].section[j].lineNum, tok);
//2nd section is opType
tok = strtok(NULL)
if(tok)
{
lines[i].section[j].opType = atoi(tok);
//3rd section is op (and so on...)
//repeat the same process here until you
//parsed and placed all members
}
}
}
}
Upvotes: 1