I have a function that manipulates a char*** using malloc and memcpy this way
// Convert a buffer full line to separated variables
int parseBufferToVariables(char ***variableContainer, char *bufferToParse, int maxVarSize) {
int i = 0;
// Get number of rows of the string
int numberOfRows = 0;
for (i = 0; bufferToParse[i] != '\0'; i++) {
if (bufferToParse[i] == '\n')
++numberOfRows;
}
// Get number of columns of the string
int numberOfColumns = 1;
for (i = 0; bufferToParse[i] != '\n'; i++) {
if (bufferToParse[i] == '\t')
++numberOfColumns;
}
// Allocate separated variable array
size_t dim0 = numberOfColumns, dim1 = numberOfRows, dim2 = maxVarSize;
variableContainer = malloc(sizeof *variableContainer * dim0);
if (variableContainer) {
size_t i;
for (i = 0; i < dim0; i++) {
variableContainer[i] = malloc(sizeof *variableContainer[i] * dim1);
if (variableContainer[i]) {
size_t j;
for (j = 0; j < dim1; j++) {
variableContainer[i][j] = malloc(sizeof *variableContainer[i][j] * dim2);
}
}
}
}
// Start parsing string to 3D array
int init = 0;
int numberOfVars = 0;
int numberOfLines = 0;
int sizeOfVar = 0;
int position = 0;
char emptyArray[MAXVARSIZE] = {0};
// Loop trought all lines
i = 0;
while (numberOfLines < numberOfRows) {
// Every delimiter
if (bufferToParse[i] == '\t' || bufferToParse[i] == '\n') {
// Size of the new sring
sizeOfVar = i - init;
// Set last \0 character in order to recognize as a proper string
memcpy(&variableContainer[numberOfVars][numberOfLines], emptyArray, maxVarSize);
// Copy the string to array
memcpy(&variableContainer[numberOfVars][numberOfLines], &bufferToParse[position], sizeOfVar);
// Handle pointers poisition
init = i + 1;
position += sizeOfVar + 1;
// Handle when end of line is reached
if (bufferToParse[i] == '\n') {
numberOfVars = 0;
numberOfLines++;
}
}
i++;
}
return numberOfRows;
}
And Im trying to call it in different ways:
char*** container= {0};
parseBufferToVariables (&container, inputString, MAXVARSIZE);
char*** container= {0};
parseBufferToVariables (container, inputString, MAXVARSIZE);
Even I try calling a char**** in the function:
int parseBufferToVariables(char**** variableContainer, char* bufferToParse, int maxVarSize)
But I always have a seg-fault calling the char*** outside the parseBufferToVariables function.
Any ideas?
OP is shooting for a 4 * parameter, yet other approaches are better.
The high degree of *s mask a key failing is that code needs to convey the column (# of tabs) width somehow.
Further, I see no certain null character termination in forming the _strings_as
the 2nd memcpy() is unbounded in size - may even overwrite allocation boundaries.
The idea below is that each level of allocation ends with a null.
csv = parse_file_string(const char *file_string);
Upon return, when csv[line] == NULL, there are no more lines
When csv[line][tab] == NULL, there are no more strings.
This approach also allows for a different number of strings per line.
Adjusted algorithm, pseudo C code
// return NULL on error
char ***parse_file_string(const char *file_string) {
number_lines = find_line_count(file_string);
char ***csv = calloc(number_lines + 1, sizeof *csv);
if (csv == NULL) return NULL;
for (line=0; line < number_lines; line++) {
tab_count = find_tab_count(file_string);
csv[line] = calloc(tab_count + 2, sizeof *(csv[line]));
// add NULL check
for (tab=0; tab < tab_count; tab++) {
char *end = strchr(file_string, '\t');
csv[line][tab] = malloc_string(file_string, end);
// add NULL check
file_string = end + 1;
}
char *end = strchr(file_string, '\n');
csv[line][tab++] = malloc_str(file_string, end);
// add NULL check
file_string = end + 1;
csv[line][tab] = NULL;
}
csv[line] = NULL;
return csv;
}
Usage
char ***container = parse_file_string(file_string);
for (line=0; container[line]; line++)
for (tab=0; container[line][tab]; tab++)
puts(container[line][tab]);
//free
for (line=0; container[line]; line++)
for (tab=0; container[line][tab]; tab++)
free(container[line][tab]);
free(container[line]);
free (container)
A pointer to a variable length array could be used if supported.
First get the dimensions of the contents of the buffer. This assumes that each line will have the same number of tabs.
Declare the pointer and allocate the memory.
Then parse the buffer into the allocated memory.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
void getdimension ( char *buffer, int *rows, int *cols, int *size) {
int maxsize = 0;
*rows = 0;
*cols = 0;
*size = 0;
while ( *buffer) {//not the terminating zero
if ( '\n' == *buffer) {
if ( ! *rows) {//no rows counted yet
++*cols;//add a column
}
++*rows;
if ( maxsize > *size) {
*size = maxsize;
}
maxsize = 0;
}
if ( '\t' == *buffer) {
if ( ! *rows) {//no rows counted yet
++*cols;
}
if ( maxsize > *size) {
*size = maxsize;
}
maxsize = 0;
}
++maxsize;
++buffer;
}
if ( '\n' != *(buffer - 1)) {//last character is not a newline
++*rows;
if ( maxsize > *size) {
*size = maxsize;
}
}
}
void createptr ( int rows, int columns, int size, char (**ptr)[columns][size]) {
if ( NULL == ( *ptr = malloc ( sizeof **ptr * rows))) {
fprintf ( stderr, "malloc problem\n");
exit ( EXIT_FAILURE);
}
for ( int line = 0; line < rows; ++line) {
for ( int tab = 0; tab < columns; ++tab) {
(*ptr)[line][tab][0] = 0;
}
}
}
void parsebuffer ( char *buffer, int rows, int columns, int size, char (*ptr)[columns][size]) {
int eachrow = 0;
int eachcol = 0;
int eachsize = 0;
while ( *buffer) {
if ( '\n' == *buffer) {
++eachrow;
eachcol = 0;
eachsize = 0;
}
else if ( '\t' == *buffer) {
++eachcol;
eachsize = 0;
}
else {
ptr[eachrow][eachcol][eachsize] = *buffer;
++eachsize;
ptr[eachrow][eachcol][eachsize] = 0;
}
++buffer;
}
}
int main ( void) {
char line[] = "12\t34\t56\t78\t!##\n"
"abc\tdef\tghi\tjkl\t$%^\n"
"mno\tpqr\tstu\tvwx\tyz\n"
"ABC\tDEF\tGHI\tJKL\tMNOPQ\n";
int rows = 0;
int columns = 0;
int size = 0;
getdimension ( line, &rows, &columns, &size);
printf ( "rows %d cols %d size %d\n", rows, columns, size);
char (*ptr)[columns][size] = NULL;//pointer to variable length array
createptr ( rows, columns, size, &ptr);
parsebuffer ( line, rows, columns, size, ptr);
for ( int row = 0; row < rows; ++row) {
for ( int col = 0; col < columns; ++col) {
printf ( "ptr[%d][%d] %s\n", row, col, ptr[row][col]);
}
}
free ( ptr);
return 0;
}
Related
I'm having some trouble with a project I'm working on for my master thesis. I got some help from a user here a while back, but the code doesn't quite seem to be working. My application crashes and saves a dump-file, but my debugging skills are quite limited so I'm not sure what is causing the crash. The code that I have that I believe is causing the crash looks like this:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stddef.h>
#include <stdbool.h>
#include "flames.h"
#include "fmodel.h"
typedef struct row_tag
{
int index;
double *data;
} row_t;
size_t get_col_count(FILE *is)
{
size_t col_count = 1;
int ch;
while ((ch = fgetc(is)) != EOF && ch != '\n')
if (ch == ',')
++col_count;
rewind(is);
return col_count;
}
row_t* csv_read(FILE *is, size_t *cols, size_t *rows)
{
*cols = get_col_count(is);
*rows = 0;
char const *origin_format = "%*[^ ,]%c";
char const *row_header_format = "%d%c";
char const *format = "%lf%c";
row_t *csv = NULL;
bool valid = true;
for (size_t current_row = 0; valid; ++current_row) {
csv = (row_t*)realloc(csv, (current_row + 1)* sizeof(row_t));
csv[current_row].data = (double*)calloc((size_t)cols - 1, sizeof(double));
for (size_t current_col = 0; valid && current_col < (size_t)cols; ++current_col) {
char delim;
if (!current_col && !current_row) {
if (fscanf(is, origin_format, &delim) != 1 || delim != ',') {
valid = false;
//continue;
}
csv[0].index = -1;
}
else if (!current_col) {
int result = -1;
if ((result = fscanf(is, row_header_format, &csv[current_row].index, &delim)) != 2 || delim != ',') {
valid = false;
continue;
}
}
else {
if (fscanf(is, format, &csv[current_row].data[current_col - 1], &delim) != 2 || delim != ',' && delim != '\n')
valid = false;
}
}
if (!valid)
free(csv[current_row].data);
else *rows = current_row + 1;
}
return csv;
}
void csv_free(row_t *csv, size_t rows)
{
for (size_t row = 0; row < rows; ++row)
free(csv[row].data);
free(csv);
}
double csv_get_value(row_t *csv, int col_index, size_t cols, int row_index, size_t rows)
{
size_t col;
for (col = 1; csv[0].data[col] != col_index && col < cols; ++col);
if (col >= cols || csv[0].data[col] != col_index)
return 0.;
size_t row;
for (row = 1; csv[row].index != row_index && row < rows; ++row);
if (row >= rows || csv[row].index != row_index)
return 0.;
return csv[row].data[col];
}
And then in my main function: (note that FMHPrint is just a printing function in the application I'm working in called FLAMES, therefore the includes aswell)
char const *filename = "Dampening.csv";
FILE *is = fopen(filename, "r");
if (!is) {
FMHPrint(0, 0, "Couldnt open \%s\ for reading!\n\n", filename);
return (FFAILURE);
}
size_t cols;
size_t rows;
row_t *csv = csv_read(is, &cols, &rows);
FMHPrint(0, 0, "Cols: %zu\n", cols);
FMHPrint(0, 0, "Rows: %zu\n", rows);
fclose(is);
for (size_t y = 0; y < rows; ++y) {
printf("%2d: ", csv[y].index);
for (size_t x = 0; x < cols - 1; ++x)
printf("%f ", csv[y].data[x]);
putchar('\n');
}
double value = csv_get_value(csv, 550, cols, 7, rows);
FMHPrint(0, 0, "Dampening value is: %f", value);
csv_free(csv, rows);
I have no idea why FLAMES keeps crashing, and the DMP-file isn't very helpful for me. Can anyone explain what is wrong? :)
Regards, Anders
You pass cols and rows as pointer to the reading function, so that you can update the variables in the calling function. When you allocate memory for each row:
csv[current_row].data = (double*)calloc((size_t)cols - 1, sizeof(double));
you use cols, which is a pointer. You probably got a compiler warning, so you decided to cast the value to size_t, but that doesn't solve the problem, it only makes the warning go away.
The value you want is where the pointer points to, so you must dereference it:
csv[current_row].data = calloc(*cols - 1, sizeof(double));
I'm stuck with sorting letters in a string. It must be sorted in alphabetical order using double pointers.
#define SIZE 21 //defined index of the array
int _tmain(int argc, _TCHAR* argv[])
{
// an array with 21 strings
char * string[SIZE] = { "dfghgfd", "rtyukljgfds", "sdsf", "fdgdfhg", "fgfhgjghj", "nmjlkjlk", "qwasazx",
"zxdfd", "opiljkg", "vcxdfgfd", "fgfhgfhgh", "bvvh", "bb", "dfsdretr",
"reuio", "cvbmhg", "fgfdyrtyty", "fgdgdfgdfgdf", "g", "fgdfg", "ghghgfhv" };
-----------------------Access to each string in array ------------------------
int Anz, i; //Anz - 21 strings
//declared new array
char** new_string;
new_string = (char**)malloc(sizeof(string));
Anz = sizeof(string) / sizeof(char*);
for (i = 0; i < Anz; i++)
{
new_string[i] = (char*)malloc(strlen(string[i]) + 1);
strcpy(new_string[i], string[i]);
}
----------------------- sorting letters--------------------------------------
char* temp;
int k, j;
for (k = 0; k<Anz - 1; k++)
{
for (j = k + 1; j<Anz; j++)
{
if (new_string[k] > new_string[j])
{
temp = new_string[k];
new_string[k] = new_string[j];
new_string[j] = temp;
}
}
}
return 0;
}
Not sure why you need the "a" array, since you can swap the characters using the new string array. Also, using a char * to hold the length of values is sort weird but I guess it works since the string lengths are pretty short.
Not sure if you wanted to sort the letters or the words. A commented section sorts the words.
Check the return of malloc as it can fail.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define SIZE 21 //defined index of the array
int main(int argc, char** argv)
{
// an array with 21 strings
char * string[SIZE] = { "dfghgfd", "rtyukljgfds", "sdsf", "fdgdfhg", "fgfhgjghj", "nmjlkjlk", "qwasazx",
"zxdfd", "opiljkg", "vcxdfgfd", "fgfhgfhgh", "bvvh", "bb", "dfsdretr",
"reuio", "cvbmhg", "fgfdyrtyty", "fgdgdfgdfgdf", "g", "fgdfg", "ghghgfhv" };
int Anz, i; //Anz - 21 strings
int width = 0, len = 0;
//declared new array
char** new_string;
Anz = sizeof(string) / sizeof(*string);
if ( NULL == ( new_string = malloc ( Anz * sizeof( *new_string)))) {
fprintf ( stderr, "malloc failed\n");
return 0;
}
for (i = 0; i < Anz; i++)
{
len = strlen ( string[i]) + 1;
if ( len > width) {
width = len;//used later when printing
}
if ( NULL == ( new_string [i] = malloc ( width))) {
fprintf ( stderr, "[i] malloc failed\n");
//free memory allocated
while ( i) {
i--;
free ( new_string[i]);
}
free ( new_string);
return 0;
}
strcpy(new_string[i], string[i]);
}
/*
//sort words
int word = 0;
while ( word < Anz - 1) {
int end = word;
int temp = end + 1;
while ( end >= 0 && 0 > strcmp ( new_string[temp], new_string[end])) {
char *hold = new_string[temp];
new_string[temp] = new_string[end];
new_string[end] = hold;
end--;
temp--;
}
word++;
}
word = 0;
while ( word < Anz) {
printf ( "Anz[%2d] is %s\n", word, new_string[word]);
word++;
}
*/
//sort letters in word
char swap;
int sorted;
int prior;
int each;
int start;
word = 0;
while ( word < Anz)
{
start = 0;//new_string[Anz][0]
sorted = start;
prior = start;
each = start + 1;//new_string[Anz][1]
printf ( "Anz[%2d] is %-*s", word, width, new_string[word]);
while ( '\0' != new_string[word][each]) {
while ( prior >= 0 && new_string[word][each] < new_string[word][prior]) {
swap = new_string[word][each];
new_string[word][each] = new_string[word][prior];
new_string[word][prior] = swap;
each--;//move toward start of string
prior--;
}
sorted++;//move toward end of string
prior = sorted;
each = prior + 1;
}
printf ( " sorted %s\n", new_string[word]);
word++;
}
//release allocated memory
word = 0;
while ( word < Anz) {
free ( new_string[word]);
word++;
}
free ( new_string);
return 0;
}
I'm trying to program a split that takes in a char-array with multiple words and separates each word into their own smaller char-array. All the pointers of the smaller char-arrays are kept in a pointer array so I can return a double pointer.
Can you take a look at my code and see if you see any errors. When I try to run my program my computer gets gradually slower, after 3-4 seconds I can't move my mouse or alt+f4 my editor. So something has to be seriously wrong!
Also I'm completely new to C-programming so I will most definitely have a silly mistake in there.
char **split(char *s) {
char **result;
int wrd_cnt = 2; //I'm adding NULL at the end of the pointer-array.
//Counts the number of words to allocate memory for the pointer-array.
for(int i = 0; i < strlen(s); i++) {
if(s[i] == ' ') {
wrd_cnt++;
}
}
result = malloc(wrd_cnt * sizeof(char*));
//Counts letters in each word to allocate memory for every single small char-array with malloc.
for(int i = 0; i < strlen(s); i++) {
for(int j = 0; j < (wrd_cnt); j++) {
int char_cnt = 0;
for(int k = 0; s[i] != ' ' || s[i] != '\0'; k++, i++) {
char_cnt++;
result[j] = malloc(char_cnt * sizeof(char));
}
}
}
//Puts each word into their own place in the pointer array.
for(int i = 0; i < strlen(s); i++) {
for(int j = 0; j < (wrd_cnt); j++) {
for(int k = 0; s[i] != ' ' || s[i] != '\0'; k++, i++) {
result[j][k] = s[i];
}
}
}
result[wrd_cnt-1] = NULL;
return result;
}
In this situation the loops using j and k can be removed and instead increment and reset i, j and char_cnt based on the i loop as the s array is processed, similar to what you had done for wrd_cnt in the first loop
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
char **split(char *s);
int main ( void) {
char **output = NULL;
int each = 0;
char line[99] = " string to parse for words ";
output = split ( line);
each = 0;
while ( output[each]) {
printf ( "%s\n", output[each]);
each++;
}
each = 0;
while ( output[each]) {
free ( output[each]);
each++;
}
free ( output);
exit ( 0);
}
char **split(char *s) {
char **result;
int wrd_cnt = 2; //I'm adding NULL at the end of the pointer-array.
int char_cnt = 0;
int i = 0;
int j = 0;
int k = 0;
//Counts the number of words to allocate memory for the pointer-array.
for(i = 0; i < strlen(s); i++) {
if(s[i] == ' ') {
wrd_cnt++;
}
}
if ( ( result = malloc(wrd_cnt * sizeof(char*))) == NULL) {
fprintf ( stderr, "malloc failure\n");
exit ( 1);
}
//Counts letters in each word to allocate memory for every single small char-array with malloc.
char_cnt = 1;
j = 0;
for( i = 0; i < strlen(s); i++) {
if ( s[i] == ' ') {
if ( ( result[j] = malloc(char_cnt * sizeof(char))) == NULL) {
fprintf ( stderr, "malloc failure\n");
exit ( 1);
}
j++;
char_cnt = 1;
continue;
}
char_cnt++;
}
if ( j == wrd_cnt - 2) {
//allocate for last word
if ( ( result[j] = malloc(char_cnt * sizeof(char))) == NULL) {
fprintf ( stderr, "malloc failure\n");
exit ( 1);
}
j++;
result[j] = NULL;
}
result[wrd_cnt - 1] = NULL;//just to make sure the last pointer is null
//Puts each word into their own place in the pointer array.
j = 0;
k = 0;
for( i = 0; i < strlen(s); i++) {
if ( s[i] == ' ') {
result[j][k] = '\0';//for space only so [j][0] is '\0'
k = 0;
j++;
continue;
}
result[j][k] = s[i];
k++;
result[j][k] = '\0';//for last word if there is no final space in s[]
}
return result;
}
Aside from the comments above your code scares me because of all the malloc() calls you do, one for each word. This means you must also free each word. This leaves programs open to memory leaks.
Given that this is C, which allows lots of casting, you can use a single malloc to hold both the (char *) pointer array AND the actual words.
char **split(char const *s) {
char **result; //
char *target; // where in result chars stored
size_t s_strlen = strlen(s); // length of s
int wrd_cnt = 2; //I'm adding NULL at the end of the pointer-array.
{
char const *sx;
for ( sx = s; sx = strpbrk( sx, " \t\n\r" ); sx++ )
{
wrd_cnt++;
}
}
result = malloc( (wrd_cnt * sizeof(char *)) + s_strlen + 2 );
/* allow for \0 and possible ' ' */
target = (char *)(result + wrd_cnt); /* where to save words */
strcpy( target, s ); /* copy to target known to be big enough */
if ( s_strlen > 0 && target[s_strlen-1] != ' ' )
strcat( target + s_strlen, " " ); /* assure ends in space */
{
char *tx, *tnext;
int n;
n = 0;
for ( tx = target; tnext = strpbrk( tx, " \t\n\r" ); tx = tnext + 1 )
{
result[n++] = tx; /* remember pointer */
*tnext = '\0'; /* terminate word */
}
result[n] = NULL; /* null termination */
}
return result;
}
I am trying to read a text file using input redirection, ./program < file.txt, and the file I made looks like this:
Country\tSport\tGender\tMedal\n
America\tCycling\tMens\tGold\n
New Zealand\tSwimming\tWomens\tSilver\n
India\tBadminton\tMens\tbronze\n
Which just reads some random data, in according to the first row with 4 columns, headed Country, Sport, Gender and Medal.
I also inserted \t and \n to make the file more readable, but the file actually has tabs and newlines in it.
I am trying to read each line of that file, and store them in an array of strings, which I have declared as:
char *records[ROWS][COLUMNS];
I would like the array of strings, records to look something like:
{{"Country", "Sport", "Gender", "Medal"}, {"America", "Cycling", "Mens", "Gold"},
{"New Zealand", "Swimming", "Womens", "Silver"}, {"India", "Badminton", "Mens", "Bronze"}}
So far I have just been using scanf to read the lines, like this:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define ROWS 1000
#define COLS 30
#define MAX_CH 50
int
main(int argc, char *argv[]) {
char *records[ROWS][COLS];
char country[MAX_CH];
char sport[MAX_CH];
char gender[MAX_CH];
char medal[MAX_CH];
while (scanf("%s\t%s\t%s\t%s\n", country, sport, gender, medal) == 4) {
printf("%s %s %s %s\n", country, sport, gender, medal);
}
return 0;
}
I know this will not work as the country name New Zealand has a space between both strings, and my scanf wil only read the first four characters. My method of scanf will also not be effective because it only works for 4 columns.
Is there a way I can use getchar() instead to do this? I'm just not sure how to use getchar to analyse each character in the input stream, and convert the necessary characters to strings depending on the tabs and newlines.
Pseudo code with getChar():
while (char = getChar()) is not 'EOF': // EOF = End of file
if char is not '\t' and char is not '\n'
save into current string
else if char is '\t'
terminate current string
increment column index
else if char is '\n'
terminate current string
increment row index
Edit:
The problem with getChar() is that you only know how long the string will be once you reach the next tab.
So you either have to iterate a first time to know the length of the string and then allocate an appropriate amount of memory, or you need to always allocate a safe amount of memory (your max string length).
Then in both options you can use strcat() to concatenate strings, but you can also access a char in a char* or char[] (String) by its index:
char string[] = "MINE"; // string[0] -> 'M'
string[0] = 'N'; // string -> "NINE"
// with dynamic memory allocation
char *string = (char*) malloc(5*sizeof(char));
string[0] = 'N'; // string -> "N"
getchar can be used to read into the rows and columns.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#define ROWS 1000
#define COLS 30
#define MAX_CH 50
void print_array(char str[][COLS][MAX_CH], int nrows, int ncols, int col[], int reorder);
void sort_array(char str[][COLS][MAX_CH], int nrows, int ncols, int col[]);
int main( int argc, char *argv[])
{
char records[ROWS][COLS][MAX_CH] = { { { '\0'}}};//set each element
char item[MAX_CH] = { '\0'};//buffer for each item
//char yn[40];
int row = 0;
int col = 0;
int usedcol = 0;
int sortby[COLS] = { 0};
int ch = 0;
int each = 0;
int loop = 0;
int result = 0;
if ( argc < 2 || argc > COLS + 1) {
fprintf ( stderr, "syntax is\n%s column0 [column1]...[column%d] < inputfile\n", argv[0], COLS - 1);
exit(EXIT_FAILURE);
}
for ( each = 1; each <= COLS + 1; each++) {// +1 to get to extra element
sortby[each - 1] = -1;//extra element will be -1
if ( each < argc) {
if ( ( result = sscanf ( argv[each], "%d", &sortby[each - 1])) != 1 || sortby[each - 1] < 0 || sortby[each - 1] >= COLS) {
fprintf ( stderr, "syntax is\n%s column0 [column1]...[column%d] < inputfile\n", argv[0], COLS - 1);
fprintf ( stderr, "column%d must be 0 to %d\n", each - 1, COLS - 1);
exit(EXIT_FAILURE);
}
}
}
for ( each = 0; each < argc - 1; each++) {
for ( loop = 0; loop < argc - 1; loop++) {
if ( loop != each && sortby[each] == sortby[loop]) {
fprintf ( stderr, "found duplicate columns in args\n");
exit(EXIT_FAILURE);
}
}
}
//printf ( "\n");
if ( isatty ( fileno ( stdin))) {
printf ( "expected that a file would be redirected to this program\n");
printf ( "syntax is\n%s column0 [column1]...[column%d] < inputfile\n", argv[0], COLS - 1);
//printf ( "enter y to continue WITHOUT the redirected file?\n");
//if ( ( fgets ( yn, sizeof ( yn), stdin))) {
//if ( strcmp ( yn, "y\n") != 0) {
exit(EXIT_FAILURE);
//}
//}
}
row = 0;
col = 0;
each = 0;
while ( ( ch = getchar ( )) != EOF) {//read each character
if ( ch == '\r') {//skip carriage return
continue;
}
if ( ch == '\t') {
strcpy ( records[row][col], item);
each = 0;//back to first character
col++;//next column
if ( col >= COLS) {
col = 0;
row++;
if ( row >= ROWS) {
fprintf ( stderr, "too many rows\n");
break;
}
}
continue;
}
if ( ch == '\n') {
strcpy ( records[row][col], item);
col++;
if ( col > usedcol) {
usedcol = col;
}
col = 0;//back to first column
each = 0;//back to first character
row++;//next row
if ( row >= ROWS) {
fprintf ( stderr, "too many rows\n");
break;
}
continue;
}
item[each] = ch;
each++;//next character
item[each] = '\0';//terminate with '\0'
if ( each >= MAX_CH - 1) {
fprintf ( stderr, "too many characters in item\n");
each = 0;
col++;
if ( col >= COLS) {
col = 0;
row++;
if ( row >= ROWS) {
fprintf ( stderr, "too many rows\n");
break;
}
}
continue;
}
}
print_array ( records, row, usedcol, sortby, 0);
sort_array ( records, row, usedcol, sortby);
print_array ( records, row, usedcol, sortby, 1);
return 0;
}
void print_array(char str[][COLS][MAX_CH], int nrows, int ncols, int col[], int reorder) {
int i, j;
int order[COLS] = { 0};
for ( i = 0; i < COLS; i++) {
if ( reorder) {
order[i] = col[i];
}
else {
order[i] = i;
}
}
if ( reorder) {
for ( i = 0; i < COLS; i++) {
for ( j = 0; j < COLS; j++) {
if ( order[j] == i) {
break;
}
if ( order[j] == -1) {
order[j] = i;
break;
}
}
}
}
for (i = 0; i < nrows; i++) {
for (j = 0; j < ncols; j++) {
printf("%-12s ", str[i][order[j]]);
}
printf("\n");
}
printf("\n");
}
void sort_array(char str[][COLS][MAX_CH], int nrows, int ncols, int col[]) {
int i = 0, j = 0, swap = 0, each = 0;
char temp[MAX_CH] = { '\0'};
do {
swap = 0;
for ( i = 1; i < nrows - 1; i++) {//iterate through rows. i=1 skip first row
for ( each = 0; col[each] != -1; each++) {//col[] has last element of -1
if ( strcmp( str[i][col[each]], str[i + 1][col[each]]) < 0) {
break;
}
if ( strcmp( str[i][col[each]], str[i + 1][col[each]]) == 0) {
continue;
}
for ( j = 0; j < ncols; j++) {//iterate through cols and swap rows
strcpy ( temp, str[i][j]);
strcpy ( str[i][j], str[i + 1][j]);
strcpy ( str[i + 1][j], temp);
}
swap = 1;
break;
}
}
} while ( swap);//loop until no swaps
}
You can read by lines and then apply strtok() using '\t' as delimiter.
Reference for strtok() (it's from cplusplus.com, but strtok is in string.h, so it works also in c)
The posted code contains several problems:
unused stack variable records[][]
unused parameter: argc
unused parameter: argv
places all input data into first entries in the arrays: country[], sport[], gender[], medal[] I.E. all the other entries are not used
does not handle 'columns' that contain any white space
includes a header file those contents are not used: string.h
The following code cleanly compiles and performs the desired function:
#include <stdio.h>
#include <stdlib.h>
//#include <string.h>
#define ROWS 1000
//#define COLS 30
#define MAX_CH 49
struct record
{
char country[ MAX_CH+1 ];
char sport [ MAX_CH+1 ];
char gender [ MAX_CH+1 ];
char medal [ MAX_CH+1 ];
};
int main( void )
{
struct record records[ROWS];
for( size_t i=0; i< ROWS; i++)
{
if( scanf(" %" MAX_CH "[^\t] %" MAX_CH "[^\t] %" MAX_CH "[^\t] %" MAX_CH "[^\n]",
records[i].country,
records[i].sport,
records[i].gender,
records[i].medal) == 4)
{
printf("%s %s %s %s\n",
records[i].country,
records[i].sport,
records[i].gender,
records[i].medal);
}
else
{
break;
}
}
return 0;
} // end function: main
I am trying to convert a vector into a string using the following function.
char* my_vect2str(char** input)
{
int i;
char* ret = (char*)xmalloc(sizeof(char*));
for(i=0; input[i] != NULL; i++)
{
if(*input[i] == '\0')
ret[i] = ' ';
else
ret[i] = *input[i];
}
ret[i] = '\0';
return ret;
}
This appears to be getting just the first character of each string in the vector. How do I alter my for loop to get this working properly? Thanks!
Your malloc should be the size of the pointer contents, not the pointer itself. You also don't need to cast the malloc void *. You need an inner loop counter in order to iterate through both dimensions of you pointer. This should work:
char* my_vect2str(char** input)
{
int i;
int count = 0;
char* ret = (char*)malloc(sizeof(char*)); // should be a larger size
for(i=0; input[i] != NULL; i++)
{
int j = 0;
while(1){
if(input[i][j] == '\0'){
ret[count++] = ' ';
break;
}else{
ret[count++] = input[i][j];
}
j++;
}
}
ret[count] = '\0';
return ret;
}
The first loop calculates the total size of the strings in input. Then, the space is allocated and the strings are concatenated to ret.
char* my_vect2str(char** input)
{
int i, j, k = 0;
char* ret;
int size = 0;
int len;
char* inp = input[k++];
while (inp != NULL) {
size += strlen(inp);
inp = input[k++];
}
ret = malloc((size * sizeof(char)) + 1);
memset(ret, 0, size + 1);
i = 0;
j = 0;
while (i < size) {
if (input[j] != NULL) {
len = strlen(input[j]);
memcpy(&ret[i], input[j], len);
i += len;
}
++j;
}
return ret;
}