DancingDylan
DancingDylan

Reputation: 53

How to count columns from within a file

I'm trying to count the number of columns from within a txt.file Example file (txt_file.txt):

aaron 1 2 3
bethi 1 2 3
dylan 1 2 3
kevin 1 2 3

This is my code snippet: (main.c)

int main(int argc, char *argv[])
{
//File based variables
char line[1024] = {0x0};
char name[100] = {0x0};
int i, j;

//Array based variables
int students = 0,
    assignments = 0,
    count = 0;

//File pointer, refer to file as fp now.
FILE *fp;
printf("the input file name is %s\n",  argv[1]);
if ( fp == NULL )
{
  puts ( "Cannot open source file");
}

// open file in read mode & get size
fp = fopen(argv[1], "r");
**get_data_size(fp,&students,&assignments);** //Function returning junk

//MY array for grades
//-------------------------------------------
int **grades = (int **)malloc(assignments * sizeof(int *));
for (i = 0; i < assignments; i++)
{
  grades[i] = (int *)malloc(students * sizeof(int));
  if (grades[i] == NULL)
  {
     fprintf(stderr, "Not enough memory to allocate\n");
     exit(1);
  }
}

Code snippet for get_data_size function: (Use: find and determine size of file. Rows vs Columns)

void get_data_size(FILE *fp, int *students, int *assignments)
{
//File based variables
char line[1024] = {0x0};
char name[100] = {0x0};

while(fgets(line, 1023, fp)) //rows
{
  students++;
}

fseek(fp, 0, SEEK_SET);
char delims[] = ",";
char *result = strtok( line, delims );
while( result != NULL )  //columns
{
  assignments ++;
  printf( "result is \"%s\"\n", result );
  result = strtok( NULL, delims );
}
//These are what return strange junk.
printf("The number of counts, or columns in the array is: %d\n",&assignments);
printf("The number of lines, or rows in the array is: %d\n",&students);
}

Upvotes: 0

Views: 3860

Answers (2)

GoingMyWay
GoingMyWay

Reputation: 17468

Try strtok() and strtok_r(), it can strip the "Abc Efg Ghi" to "Abc", "Efg", "Ghi", and you can count the total column and store "Abc", "Efg", "Ghi" in a 2-D array.

char *strtok(char *str, const char *delim);
char *strtok_r(char *str, const char *delim, char **saveptr);

The strtok() function returns a pointer to the next "token" in str1, where str2 contains the delimiters that determine the token. strtok() returns NULL if no token is found. In order to convert a string to tokens, the first call to strtok() should have str1 point to the string to be tokenized. All calls after this should have str1 be NULL.

Also, you can read the docs

For example:

char str[] = "Abc Efg Ghi";
char delims[] = " ";
char *result = NULL; 
int count = 0;
result = strtok( str, delims );
while( result != NULL ) {
    count ++;
    printf( "result is \"%s\"\n", result );
    result = strtok( NULL, delims );
}
printf("The total column is %d\n", count);

The above code will display the following output:

result is "Abc "
result is "Efg"
result is "Ghi"
The total column is 4

The following code read text from a file named test.csv, and store the data into 2-D array.

test.csv

alice, 22, 1992/03/05
bob, 33, 1981/11/21
cart, 40, 1974/07/13

readfile.c

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

char *trim(char *str)
{
    char *p = str; 
    while (*p == ' ' || *p == '\t' || *p == '\r' || *p == '\n')
        p ++;
    str = p; 
    p = str + strlen(str) - 1; 
    while (*p == ' ' || *p == '\t' || *p == '\r' || *p == '\n')
        -- p;
    *(p + 1) = '\0'; 
    return str; 
}
int main()
{
    int i, j;
    char ** data = (char **)malloc(100 * sizeof(char *));
    for (i = 0; i < 100; i ++)
    {
        data[i] = (char *)malloc(20 * sizeof(char));
    }
    char *** mydata = (char ***)malloc(3 * sizeof(char **));
    for (i = 0; i < 3; i ++)
    {
        mydata[i] = (char **)malloc(3 * sizeof(char *));
        for (j = 0; j < 3; j ++)
        {
            mydata[i][j] = (char *)malloc(20 * sizeof(char));
        }
    }

    FILE *fp = fopen("test.csv", "r");
    if(fp == NULL) {
        return -1;
    }

    char line[1024];
    int row = 0;
    while(fgets(line, sizeof(line), fp)) 
    {
        //printf("%s", line);

        char *save_ptr;
        char *name = strtok_r(line, ",", &save_ptr);
        if (name == NULL) {
            return -1;
        }
        //printf("%s\n", save_ptr);
        char *age = strtok_r(NULL, ",", &save_ptr);
        //printf("%s\n", save_ptr);
        char *birthday = strtok_r(NULL, ",", &save_ptr);
        //printf("%s\n", save_ptr);
        strcpy(mydata[row][0], trim(name));
        strcpy(mydata[row][1], trim(age));
        strcpy(mydata[row][2], trim(birthday));
        //printf("%s\t%s\t%s\n", trim(name), trim(age), trim(birthday));
        row ++;
    }
    for (row = 0; row < 3; row ++)
    {
        printf("%s\t%s\t%s\n", mydata[row][0], mydata[row][1], mydata[row][2]);
    }
    return 0;
}

The function split the ,, and you can also split the " ". You can change the code as your need.

Hope these two function can help you to store your data and get the column number.

Upvotes: 1

David C. Rankin
David C. Rankin

Reputation: 84561

A very simple way to count the columns of data in a string is to walk a pointer down the string and check each character. If a non-whitespace character is encountered, increase you count by 1 and then scan to find the next whitespace. Then simply scan forward while the characters continue to be whitespace until you find the next non-whitespace character and (repeat). A quick function that fits that bill could be something like:

/** count columns of data in a character string.
 */
int countcol (const char *s)
{
    if (!s || !*s) return 0;    /* validate input */

    char *p = s;    /* pointer to buf */
    int col = 0;    /* initialize col */

    while (*p) { /* for each char in buf */
        /* if p points to non-whitespace, col + 1 */
        if (*p != ' ' && *p != '\t') col++, p++;
        /* skip non-whitespace to find whitespace */
        while (*p && *p != ' ' && *p != '\t') p++;
        /* skip multiple whitespace together */
        while (*p && (*p == ' ' || *p == '\t')) p++;
    }
    return col;
}

A small sample program that reads information on stdin and counts the columns in each line could be:

#include <stdio.h>

enum { MAXC = 64 };

void rmcrlf (char *str);
int countcol (const char *s);

int main (void) {

    char buf[MAXC] = "";
    int col = 0;

    while (fgets (buf, MAXC, stdin)) {  /* read from stdin    */

        rmcrlf (buf);   /* remove trailing '\n' read by fgets */
        if (!*buf) continue;            /* skip empty line    */
        col = countcol (buf);
        printf ("'%s' (%d-col)\n", buf, col);
    }

    return 0;
}

/** stip trailing newlines and carraige returns by overwriting with
 *  null-terminating char. str is modified in place.
 */
void rmcrlf (char *str)
{
    if (!str || !*str) return;
    char *p = str;
    for (; *p; p++)
        if (*p == '\n' || *p == '\r') { *p = 0; break; }
}

/** count columns of data in a character string.
 */
int countcol (const char *s)
{
    if (!s || !*s) return 0;    /* validate input */

    char *p = s;    /* pointer to buf */
    int col = 0;    /* initialize col */

    while (*p) { /* for each char in buf */
        /* if p points to non-whitespace, col + 1 */
        if (*p != ' ' && *p != '\t') col++, p++;
        /* skip non-whitespace to find whitespace */
        while (*p && *p != ' ' && *p != '\t') p++;
        /* skip multiple whitespace together */
        while (*p && (*p == ' ' || *p == '\t')) p++;
    }
    return col;
}

(note: adjust the constant MAXC to meet your input length; also note the rmcrlf function simply removes an carriage-returns or line-feeds ('\n') from the end of the string read by fgets by overwriting either character with a 0 (the nul-terminating character))

Input File

$ cat dat/text_file.txt
aaron 1 2 3
bethi 1 2 3
dylan 1 2 3
kevin 1 2 3

Use/Output

$ ./bin/file_count_col <dat/text_file.txt
'aaron 1 2 3' (4-col)
'bethi 1 2 3' (4-col)
'dylan 1 2 3' (4-col)
'kevin 1 2 3' (4-col)

Let me know if you have any questions.

Upvotes: 2

Related Questions