sort out IP addrs from file in C program

93 Views Asked by At

So, I have to create a .c file which sorts out IP addresses in ascending order and gives a count to all ip adresses. Considering there is ip.txt file present while has content like below (IP Address and ErrorCode)

127.0.0.0 500
127.0.0.0 400
127.0.0.1 300
127.0.0.2 100

Please suggest something If you're good with Programming, Advance Thanks!

Below is trial that I performed, Feel free to suggest and modify code for perfect output.

Tried code is attached in Image file, I'm expecting to sort out IP addresses and give count of IP addresses from Max to Min in output console.

File_Operation_Code Below

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

int main(){
    char * line = NULL;
    size_t len = 0;
    ssize_t read;
    int count=0, totalCh=0;

    FILE *fp = fopen("ip.txt", "r");
    if(fp == NULL){
        printf("IP File does not exist!\n");
        return -1;
    }

    while ((read = getline(&line, &len, fp)) != -1) {
        count++;
        totalCh+=read;
        printf("line of length: %zu\n", read);
        printf("line content: %s", line);
    }

    printf("Total number of lines=%d\n", count);
    printf("Total number of characters=%d\n", totalCh);

    // logic to fetch IP addr and sort out and give count to output console pending

    fclose(fp);
    if (line)
        free(line);

    return 0;
}
1

There are 1 best solutions below

2
arfneto On

If you write a suitable compare function, then you can just use qsort to sort the data.

But you may not need even this. Below is an example of a way to go from the file to the result in a safe way.

IPv4 address for a host

An IP (v4) address is a group of 4 octets. For a host you can not have 0 as the 1st octet or for the last one. Also you can not have all ones --- 255. 0 is the network address and 255 is the broadcast address.

encapsulating: a Packet for a valid IP and code

typedef struct
{
    char     ip[16];
    int      a, b, c, d;  // octets
    unsigned code;
} Packet;

This is ok for a rapid test and makes life easier. Since this is just toy we can have both the string and the decimal values for the octets.

This function so_compare() compares 2 Packet and return -1 if one comes before other in sorting order. And +1 if one goes after other. This is all you need to sort them using qsort from stdlib in C.

int so_compare(Packet* one, Packet* other)
{
    if (one == NULL) return 0;
    if (other == NULL) return 0;
    // 1
    if (one->a < other->a) return -1;
    if (one->a > other->a) return 1;
    // 2
    if (one->b < other->b) return -1;
    if (one->b > other->b) return 1;
    // 3
    if (one->c < other->c) return -1;
    if (one->c > other->c) return 1;
    // 4
    if (one->d < other->d) return -1;
    if (one->d > other->d) return 1;
    // code
    if (one->code < other->code) return -1;
    if (one->code > other->code) return 1;
    // stable
    return 1;
}

And you just need to get one for each valid line in the input file.

validating a line

This can be very simple: we just need a valid IP and a valid code. sscanf() is good here, since we can parse all 5 values at once.

a simple function to parse a line

so_get_packet() below takes a line from the input file and returns a valid Packet, in a few lines of code.

Extracts the values for the octets, validates them, fills in a new Packet and we are done.

Packet* so_get_packet(const char* addr)
{
    int         a, b, c, d;
    int         code;
    int         res  = 0;
    const char* mask = "%3d.%3d.%3d.%3d %3d";
    res = sscanf(addr, mask, &a, &b, &c, &d, &code);
    if (res != 5) return NULL;
    if ((a < 1) || (a > 254)) return NULL;
    if ((b < 0) || (b > 254)) return NULL;
    if ((c < 0) || (c > 254)) return NULL;
    if ((d < 1) || (d > 254)) return NULL;
    Packet* pck = malloc(sizeof(Packet));
    if (pck == NULL) return NULL;
    sprintf(pck->ip, "%d.%d.%d.%d", a, b, c, d);
    pck->a = a, pck->b = b, pck->c = c, pck->d = d;
    pck->code = code;
    return pck;
}

storing valid packets

This is C. Life is easier with composition and containment. Consider a sequence of packets and we can use Vector as below to store them.

#define MAX_DATA 100
typedef struct
{
    size_t size;
    size_t limit;
    Packet data[MAX_DATA];
} Vector;

So the program is just a matter of

  • read the file
  • create a Packet for each valid line data
  • inserting packets into a Vector thing
  • sorting the data

As each Vector has a size we always know the number of addresses we got so far.

no need to qsort the data

Since we get one line at a time makes more sense to just insert them in order...

int so_insert(Packet* pck, Vector* V)
{
    if (pck == NULL) return -1;
    if (V == NULL) return -2;
    if (V->size == V->limit) return -3; // full
    if (V->size == 0) // was empty
    {
        V->data[0] = *pck;
        V->size = 1;
        return 0;
    }
    for (size_t pos = V->size; pos > 0; --pos)
    {   // insert in position
        if (so_compare(pck, &V->data[pos - 1]) > 0)
        {   // 'pos' is the position
            V->data[pos] = *pck;
            ++V->size;
            return 0;
        }
        V->data[pos] = V->data[pos - 1];
    };  // for
    V->data[0] = *pck;
    ++V->size;
    return 0;
}

No surprise here: so_insert() as above inserts a Packet into a Vector, in order, so when we reach the end on input all lines are already sorted in Vector.

consuming a file

This so_consume_file() does the expected: gets a file name and returns a sorted list of addresses and codes. Or NULL in case of error.

Vector* so_consume_file(const char* file_name)
{
    FILE* in = fopen(file_name, "r");
    if (in == NULL) return NULL;
    // create array
    Vector* data = so_create(MAX_DATA);
    if (data == NULL) return NULL;

    char  buffer[256] = {0};
    char* p           = buffer;
    int   res         = 0;
    while ((p = fgets(buffer, sizeof(buffer), in)) != NULL)
    {
        Packet* pck = so_get_packet(buffer);
        if (pck != NULL)
        {
            so_insert(pck, data);
            free(pck);
        }
    };
    fclose(in);
    so_show_v(data, "final data ");
    return data;
}

And is easy to read: a single loop. If a line has a packet it gets into the list. At the end the list is printed and destroyed.

full code for a test

#define MAX_DATA 100

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

typedef struct
{
    char     ip[16];
    int      a, b, c, d;  // octets
    unsigned code;
} Packet;

typedef struct
{
    size_t size;
    size_t limit;
    Packet data[MAX_DATA];
} Vector;

Vector* so_create(size_t);
Vector* so_destroy(Vector*);
Vector* so_consume_file(const char*);
int so_insert(Packet*, Vector*);
int     so_show_v(Vector*,const char*);

Packet* so_get_packet(const char*);
int     so_compare(Packet*, Packet*);
int     so_show_p(Packet*, const char*);

// https://stackoverflow.com/questions/77597529/
// sort-out-ip-addrs-from-file-in-c-program

int main(int argc, char** argv)
{
    const char* def_name = "in.txt";
    char        f_name[50];
    if (argc < 2)
        strcpy(f_name, def_name);
    else { strcpy(f_name, argv[1]); }
    fprintf(stderr, "input file is \"%s\"\n", f_name);
    Vector* my_data = so_consume_file(f_name);
    so_destroy(my_data);
    return 0;
}

Vector* so_create(size_t limit)
{
    if (limit > MAX_DATA) return NULL;
    Vector* one = malloc(sizeof(Vector));
    if (one == NULL) return NULL;
    one->limit = limit;
    one->size  = 0;
    return one;
}

Vector* so_destroy(Vector* del)
{
    if (del == NULL) return NULL;
    free(del);
    return NULL;
}

Vector* so_consume_file(const char* file_name)
{
    FILE* in = fopen(file_name, "r");
    if (in == NULL) return NULL;
    // create array
    Vector* data = so_create(MAX_DATA);
    if (data == NULL) return NULL;

    char  buffer[256] = {0};
    char* p           = NULL;
    while ((p = fgets(buffer, sizeof(buffer), in)) != NULL)
    {
        Packet* pck = so_get_packet(buffer);
        if (pck != NULL)
        {
            so_insert(pck, data);
            free(pck);
        }
    };
    fclose(in);
    so_show_v(data, "final data ");
    return data;
}

int so_insert(Packet* pck, Vector* V)
{
    if (pck == NULL) return -1;
    if (V == NULL) return -2;
    if (V->size == V->limit) return -3; // full
    if (V->size == 0) // was empty
    {
        V->data[0] = *pck;
        V->size = 1;
        return 0;
    }
    for (size_t pos = V->size; pos > 0; --pos)
    {   // insert in position
        if (so_compare(pck, &V->data[pos - 1]) > 0)
        {   // 'pos' is the position
            V->data[pos] = *pck;
            ++V->size;
            return 0;
        }
        V->data[pos] = V->data[pos - 1];
    };  // for
    V->data[0] = *pck;
    ++V->size;
    return 0;
}

int so_show_v(Vector* V, const char* msg) 
{
    if (V == NULL) return -1; // no vector
    if (msg != NULL) printf("%s", msg);
    printf("%llu/%llu elements\n", V->size, V->limit);
    for (size_t i = 0; i < V->size; ++i)
    {
        so_show_p(&V->data[i], NULL);
    }
    printf("---\n\n");
    return 0;
}

Packet* so_get_packet(const char* addr)
{
    int         a, b, c, d;
    int         code;
    int         res  = 0;
    const char* mask = "%3d.%3d.%3d.%3d %3d";
    res = sscanf(addr, mask, &a, &b, &c, &d, &code);
    if (res != 5) return NULL;
    if ((a < 1) || (a > 254)) return NULL;
    if ((b < 0) || (b > 254)) return NULL;
    if ((c < 0) || (c > 254)) return NULL;
    if ((d < 1) || (d > 254)) return NULL;
    Packet* pck = malloc(sizeof(Packet));
    if (pck == NULL) return NULL;
    sprintf(pck->ip, "%d.%d.%d.%d", a, b, c, d);
    pck->a = a, pck->b = b, pck->c = c, pck->d = d;
    pck->code = code;
    return pck;
}

int so_compare(Packet* one, Packet* other)
{
    if (one == NULL) return 0;
    if (other == NULL) return 0;
    // 1
    if (one->a < other->a) return -1;
    if (one->a > other->a) return 1;
    // 2
    if (one->b < other->b) return -1;
    if (one->b > other->b) return 1;
    // 3
    if (one->c < other->c) return -1;
    if (one->c > other->c) return 1;
    // 4
    if (one->d < other->d) return -1;
    if (one->d > other->d) return 1;
    // code
    if (one->code < other->code) return -1;
    if (one->code > other->code) return 1;
    // stable
    return 1;
}

int so_show_p(Packet* P, const char* msg)
{
    if (P == NULL) return -1;
    if (msg != NULL) printf("%s", msg);
    printf("     %16s %4d\n", P->ip, P->code);
    return 0;
}

The program accepts a file name as input. Default is in.txt

example output

SO> cat in.txt

stack overflow

127.0.0.0 500 not a host
127.0.0.0 400 not a host
127.0.0.1 300
127.0.0.2 100
127.0.0.2 99
127.0.0.2 101
127.0.0.2 100

SO> v1
input file is "in.txt"
final data 5/100 elements
            127.0.0.1  300
            127.0.0.2   99
            127.0.0.2  100
            127.0.0.2  100
            127.0.0.2  101
---


SO>

SO>


SO> cat other.txt
127.0.0.10 500
127.0.0.100 400
127.0.0.1 300
127.0.0.2 100
127.0.0.2 101
127.0.0.2 101
10.0.0.1 200
10.0.0.1 201
10.0.0.92 200
10.0.0.2 200

SO> v1 other.txt
input file is "other.txt"
final data 10/100 elements
             10.0.0.1  200
             10.0.0.1  201
             10.0.0.2  200
            10.0.0.92  200
            127.0.0.1  300
            127.0.0.2  100
            127.0.0.2  101
            127.0.0.2  101
           127.0.0.10  500
          127.0.0.100  400
---


SO>

Not really tested. Hope it helps as showing a way to go from the raw file data to a prototype in a controlled way.