0

I didn't have any error when running the codes below. But on the command console it seems to be stuck on Processing filename xxxxx as below.

May I ask how do I use if condition to filter out number of data characters of less than 50 in line 67, I try to use strlen(array[count].data) >= 1 && strlen(array[count].data) <= 50 but doesn't seem to work ?

Enter filename: testdata.txt

Processing filename testdata.txt ...

Sample Source txt file:

9001:0002:9003:0021:CLS
0001:0010:0003:0021:CLS
8001:0002:8002:0080:<HTML>
0001:4002:0002:0080:<BODY>
0004:0002:0002:0080:JHJKJBKHJBIUHBKBKHBKHHBKJBKJNKJKHHKUHKJLHBKHBKHBHBHBKHBHBHBHBBHHBHBJKJHKJHKJHKUHIUJ          

Source code:

#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>  
#include <stdlib.h> 
#include <malloc.h>

//struct definition
struct record{
int src;
int dest;
int type;
int port;
char data[100];
};


int main()
{
struct record *array;

FILE* inFile; //file handle or pointer
FILE* outFile;
FILE* errorFile;
char filename[100];

int count = 0;  //keep track of number of records in memory
int i = 0;
int test;
int n1 = 0; //keep track number of correct records
int n2 = 0; //keep track number of error records

array = (struct record *) malloc(sizeof(struct record));

//User to enter filename
printf("Enter filename: ");
scanf("%s", filename);

printf("Processing filename %s ...\n", filename);
inFile = fopen(filename, "r");
if (inFile == NULL) //check if file handler is invalid
{
    printf("Could not open file %s\n", filename);
    exit(1); //error status code
}

test = fscanf(inFile, "%d:%d:%d:%d",
    &array[count].src, &array[count].dest, &array[count].type, &array[count].port);
fgets(array[count].data, 100, inFile);
while (test != EOF){
    count++;
    array = (struct record *) realloc(array, (count + 1)*sizeof(struct record));
    test = fscanf(inFile, "%d:%d:%d:%d:%s",
        &array[count].src, &array[count].dest, &array[count].type, &array[count].port, &array[count].data);
}
fclose(inFile); //must always close file once done

outFile = fopen("data_1.txt", "wt");
errorFile = fopen("data_error.txt", "wt");
if (outFile == NULL) //check if file handler is invalid
{
    printf("Could not write to file \n", filename);
    exit(1);
}

if (count > 0){
    printf("Viewing all records: ", count);
    for (i = 0; i < count; i++){
        if (array[count].src >= 1 && array[count].src <= 1024 && array[count].dest >= 1 && array[count].dest <= 1024 && array[count].type >= 1 && array[count].type <= 10 && array[count].port >= 1 && array[count].port <= 1024)
            n1++;
            fprintf(outFile, "%d %d %d %d",
            (i + 1),
            array[count].src,
            array[count].dest,
            array[count].type,
            array[count].port
            );
    }
}
else
{
    n2++;
    fprintf(errorFile, "%d %d %d %d",
        (i + 1),
        array[count].src,
        array[count].dest,
        array[count].type,
        array[count].port
        );
}

fclose(errorFile);
fclose(outFile);
return 0;
}
Doran L
  • 299
  • 2
  • 5
  • 19
  • `fscanf` returns `int` not `char` , so `test` should be `int` . – ameyCU Nov 25 '15 at 04:47
  • You are missing an `fgets` inside the `while` loop. So likely `test` is going to be continuously `0` and hence the loop will be infinete. May I suggest that you use a debugger to help you find such problems? Or even basic `printf` debug statements. Learning to debug your own code is a useful long term skill. – kaylum Nov 25 '15 at 04:47
  • @kaylum OP is using `fscanf` and stores its `return` into `test` inside `while` loop , so that wont be problem here I think . And how does `fgets` come here ? – ameyCU Nov 25 '15 at 04:52
  • @ameyCU Have a closer look. The `fscanf` only gets the first 4 columns of the input. The last column of the input is a string and OP is using `fgets` to get that. Not how I would do it but that's the way the code is. BTW, I agree that your pick up regarding `test` type is indeed a bug and needs to be fixed too. – kaylum Nov 25 '15 at 04:54
  • I change the test to int but got the error as below. I google and confirm that I launch this project as console application. I updated the question with the latest code -> 1>------ Build started: Project: Project, Configuration: Debug Win32 ------ 1>LINK : fatal error LNK1168: cannot open D:\Dropbox\Laptop\School\C++\Project\Debug\Project.exe for writing – Doran L Nov 25 '15 at 05:08
  • @kaylum `fscanf` inside loop also reads the string with `4` integers , and therefore no need of `fgets` inside loop . OP mixed up things between that . – ameyCU Nov 25 '15 at 05:09
  • @ameyCU OP just changed it :-) The `fscanf` didn't have that last string read when I commented. – kaylum Nov 25 '15 at 05:10
  • @DoranL Please don't change the code on us like that. It invalidates all the comments. If you must, provide an update below your original code. – kaylum Nov 25 '15 at 05:10
  • @DoranL See this , this creates confusion , please don't so such things . And even changed data type of `test` . – ameyCU Nov 25 '15 at 05:11
  • Oops, sorry about that ... – Doran L Nov 25 '15 at 05:12
  • @DoranL Your new error has nothing to do with the code change. It means you probably have a window open somewhere that is running the program so the compiler cannot overwrite the binary. – kaylum Nov 25 '15 at 05:14
  • Thanks i saw that. the code is working now. Would you be able to advise how could i filter the string length for data ? – Doran L Nov 25 '15 at 05:17
  • Actually, do you want to keep strings <50 characters, or do you want to throw those away or do you want to store just the first 50 characters? It's not clear from your description. – kaylum Nov 25 '15 at 05:23
  • For any lines that's < 50 it will be fprintf to outFile. for lines containing >50 characters it will be fprintf to errorFile. The bottom if else statement is not complete yet. – Doran L Nov 25 '15 at 05:26
  • Then the `strlen` conditions you have shown are fine. If you can't get it to work then you need to explain what the incorrect behaviour is (more than just "it doesn't work"). Probably should start a new question for that as this one has gone south. – kaylum Nov 25 '15 at 05:31
  • Thanks .. will post it in another question – Doran L Nov 25 '15 at 05:33
  • @DoranL please indent your code. – Jabberwocky Feb 29 '16 at 12:43

1 Answers1

0

There are many common errors in this. Which book are you reading?

array = (struct record *) malloc(sizeof(struct record));
test = fscanf(inFile, "%d:%d:%d:%d",
&array[count].src, &array[count].dest, &array[count].type, &array[count].port);
fgets(array[count].data, 100, inFile);
while (test != EOF){
    count++;
    array = (struct record *) realloc(array, (count + 1)*sizeof(struct record));
    test = fscanf(inFile, "%d:%d:%d:%d:%s", &array[count].src,  &array[count].dest
                                          , &array[count].type, &array[count].port
                                          , &array[count].data);
}

Forgive me. I took the liberty to clean up a bit of your code and condense some of the logic.

You shouldn't cast the return value of malloc or realloc.

While I'm on the topic of types, isn't your compiler warning you about the type of &array[count].data being incompatible with the type that corresponds to %s? Don't cast it. Just remove the & and recognise how the expression array[count].data that denotes an array is converted to a pointer of the appropriate type.

x = realloc(x, ...); where x is whatever expression is (almost) always incorrect. You need to use a temporary variable so you can handle errors correctly without leaking memory (example below)... While I'm on the topic of realloc usage, the first argument can be NULL and realloc will act like malloc in that case, so technically that first malloc is unnecessary bloat...

struct record *array = NULL;
void *temp = realloc(array, (count + 1) * sizeof *array);
if (!temp) {
    puts("ERROR ALLOCATING MEMORY!");
    exit(EXIT_FAILURE);
}
array = temp;

Have you considered the difference between fgets and fscanf("%s", ...)? Are you aware that the former is for reading lines, while the latter is for reading words? This would explain your problem; fscanf is halting when it sees a space, leaving everything after the space to be processed by the next fscanf call. The result? An infinite loop of fscanf fails.

Have you considered that not all fscanf fails return EOF? If you ask fscanf to read an int (using %d) and it immediately encounters non-decimal digits it can't proceed any further; a conversion failure occurs.

If you want to ensure that fscanf reads all 5 fields successfully, you need to compare the return value to 5, not to EOF.

I think you most likely meant to read the remainder of the line following your four colon-separated ints, and here's how I'd naively do that:

array = NULL;
struct record record;
while (fscanf(inFile, "%d:%d:%d:%d:%99[^\n]", &record.src,  &record.dest
                                            , &record.type, &record.port
                                            ,  record.data) == 5) {
    /* It's possible that a user might enter more than   *
     * 99 chars for the last field, so when that happens *
     * we should discard the remainder...                */
    if (getchar() != '\n') {
        puts("WARNING: Maximum field length for data is 99; user input truncated");
        fscanf(inFile, "%*[^\n]");
    }

    void *temp = realloc(array, (count + 1) * sizeof *array);
    if (!temp) {
        puts("ERROR ALLOCATING MEMORY!");
        exit(EXIT_FAILURE);
    }
    array = temp;
    array[count++] = record;
}
Community
  • 1
  • 1
autistic
  • 1
  • 3
  • 35
  • 80