c - grouping strings in a struct - c

I have a bunch of strings that look like:
'Hello1-FOO', 'Aello2-FOO', 'Bye1-BAR', 'Bye3-BAR', 'Hello22-FOO', 'Bye4-BAR', 'Welcome-BAR' ...
All of them are stored on a struct.
struct str {
char *strings;
}
...
struct str **t_str;
size_t j;
t_str = malloc(sizeof *t_str * 20);
for (j = 0; j < 20; j++)
t_str[j] = malloc(sizeof *t_str[j]);
...
t_str[0]->strings = "Hello1-FOO";
t_str[1]->strings = "Aello2-FOO";
....
What I would like to do is to display (sort) them by category, so they look similar to this:
FOO:
Hello1-FOO
Aello2-FOO
Hello22-FOO
BAR:
Bye4-BAR
Welcome-BAR
Bye1-BAR
Bye3-BAR
Basically group them by the token after the '-'
What would be a good way of doing this? Should I store them on a second struct after processing the string? Any idea will be appreciated. Thanks

Just use qsort. The following code makes some assumptions but you should be able to change it to suit your needs.
int categoryComparitor(const void * a, const void * b)
{
char *string1 = (char *)a;
char *string2 = (char *)b;
string1 = strrchr(string1, '-') + 1;
string2 = strrchr(string2, '-') + 1;
return strcmp(string1, string2);
}
{
...
char *strings[]; // Array of string pointers
int stringCount; // Holds current number of valid elements in strings.
...
qsort(strings, stringCount, sizeof(char *), categoryComparitor);
}

As David Thornley already pointed out, your struct isn't really defined well to handle this situation (well at all). Since your input is two separate logical pieces, you really want to define the struct accordingly -- containing two separate strings, one for each part of the input.
struct record {
char *category;
char *string;
};
Then you want to read each piece into one of those two strings:
record read_line(FILE *infile) {
char buffer1[128], buffer2[128];
fscanf(infile, "%[^-]-%s", buffer1, buffer2);
record ret;
ret.string = dupe_string(buffer1);
ret.category = dupe_string(buffer2);
return ret;
}
Then, to sort those records, you'll want to define a comparison function with the signature expected by qsort, that does a comparison on the category member:
int cmp(void *a, void *b) {
return strcmp(((record *)a)->category, ((record *)b)->category);
}
Then you'll sort your array of records using that comparison function.

Related

C - how to return more than one char

Anyone know how to return 3 char values from a function to the main part? The three chars have to be input by the user and made into a pyramid. I have been told to use pointers but don't understand how to do it and keep getting errors. These 3 characters then have to be used in the main function to make a pyramid shape.
To expand on my comment, there are basically two ways I would recommend solving this problem, of which the first comes in two variants (depending on use-case and requirements).
Pass an array as argument to the function, and have it filled in. This comes with two variants depending on what is needed:
Just use an ordinary array
void doSomething(char *array)
{
array[0] = 'A';
array[1] = 'B';
array[2] = 'C';
}
Call like
char array[3];
doSomething(array);
printf("First character is '%c'\n", array[0]);
The second variant is basically the same as above, but treats the array as a (null-terminated) string:
void doSomething(char *array)
{
strcpy(array, "ABC");
}
Call like
char array[4]; // One extra for terminator
doSomething(array);
printf("String is '%s'\n", array);
The second way to return multiple values is through the use of structures:
struct Data
{
char a;
char b;
char c;
};
struct Data doSomething(void)
{
struct Data data;
data.a = 'A';
data.b = 'B';
data.c = 'C';
// or struct Data data = { 'A', 'B', 'C' };
return data;
}
Call like
struct Data data = doSomething();
printf("First character is '%c'\n", data.a);
I write an example of pointers, I hope it's useful:
main(){
int a,b,c;
doSomething(&a,&b,&c);
}
void doSomething(int *x, int *y, int *z){
//example
*x = 5;
*y = 7;
*z = 10;
}

Function to modify dynamically allocated two dimensional array of char*

So, here's my logic:
This is some text:
char *text;
Then this is array of texts:
char **arr;
Then array of these arrays is:
char ***arr2d;
And if I want a function to modify it, it needs to accept it as:
char ****arr2d;
And within the function use it as:
*arr2d = (e.g. allocate);
So if I want to create 2D array like this and make the first row, first column contain just a letter 'a', then why does this not work?
#define COLUMNS 7
void loadTable(char ****table)
{
*table = (char ***) malloc(sizeof(char**));
if (!*table) {
printf("Allocation error for table rows.");
return;
}
*table[0] = (char**) malloc(COLUMNS * sizeof(char*));
if (!*table[0]) {
printf("Allocation error for table columns.");
return;
}
*table[0][0] = (char*) malloc(2 * sizeof(char));
*table[0][0][0] = (char)(97);
*table[0][0][1] = '\0';
}
int main()
{
char ***table;
loadTable(&table);
return 0;
}
You would need only 3 *** to do what you describe, not 4 ****. Be aware, there are methods to allow you to avoid excessive depth in terms of arrays of arrays of strings. And there are also good reasons to avoid excessively deep orders of arrays, not the least is the need to free(.) everything you allocate. That means exactly one call to free(.) for each and every call to [m][c][re]alloc(.).
But to address your question...
In general, to create new memory for a single array of a previously allocated memory, you could use a function prototyped as:
char * ReSizeBuffer(char **str, size_t origSize);
Where if say the previously allocated buffer were created as:
char *buf = calloc(origSize, 1);
...the usage could look like:
char *tmp = {0};
tmp = ReSizeBuffer(&buf, newSize); //see implementation below
if(!tmp)
{
free(buf);
return NULL;
}
buf = tmp;
///use new buf
...
Then if this works for a single array of char, then the prototype for allocating new memory for a previously allocated array of strings might look like this:
char ** ReSizeBuffer(char ***str, size_t numArrays, size_t strLens);
Where if say the previously allocated 2D buffer were created as:
char **buf = Create2DStr(size_t numStrings, size_t maxStrLen); //see implementation below
...the usage could look like:
char **tmp = {0};
tmp = ReSizeBuffer(&buf, numStrings, maxStrLen);
if(!tmp)
{
free(buf);
return NULL;
}
buf = tmp;
///use new buf
...
Implementations:
Implementation of ReSizeBuffer. This must be expanded if you desire to implement the second prototype:
char * ReSizeBuffer(char **str, size_t size)
{
char *tmp={0};
if(!(*str)) return NULL;
if(size == 0)
{
free(*str);
return NULL;
}
tmp = (char *)realloc((char *)(*str), size);
if(!tmp)
{
free(*str);
return NULL;
}
*str = tmp;
return *str;
}
Implementation of Create2DStr might look like this:
char ** Create2DStr(size_t numStrings, size_t maxStrLen)
{
int i;
char **a = {0};
a = calloc(numStrings, sizeof(char *));
for(i=0;i<numStrings; i++)
{
a[i] = calloc(maxStrLen + 1, 1);
}
return a;
}
I just stumbled upon this old question of mine and spotted the problem immediately. Here is the answer:
The number of asterisks is actually correct, the problem is operator evaluation. Specifically, all lines of code of this form:
*table[0]
should have been written as:
(*table)[0]

Sorting a 2D char array but by number

I have been searching for a solution but can't seem to find one similar to mine. Am trying to sort
a 2D char * array by a certain column.
char *objs[50][3];
/***** within a loop to populate with values *****/
objs[count][0]=obj->level; //this is a number to be sorted
objs[count][1]=obj->cost; //this is a number
objs[count][2]=obj->short_desc->str; //this is a string
count++;
/***** end loop *********/
qsort(objs, count, sizeof(char *), compare_function); //to sort by obj->level, int values
i deleted my previous solution because it was showing all kinds
of weird numbers or not even sorting. i am not very experienced with
C, and would greatly appreciate help on how to do this.
thank you in advance.
When sorting implicit structs in "C" defined by 2d arrays, where each row corresponds to a single object, I find it useful to use qsort_s() because it allows me to pass in more information about the the array entries to be sorted into the comparison function.
Thus the following accepts a 2d array of strings with nRows rows and nColumns columns and sorts on a column specified by sortColumnIndex. The extra context pointer provided to qsort_s() communicates the sort index down to the sorting method without requiring global variables:
struct sort_on_index_context
{
size_t nRows;
size_t nColumns;
size_t sortColumnIndex;
};
static int compare(void *p_vcontext, const void *ventry1, const void *ventry2)
{
struct sort_on_index_context *p_context = (struct sort_on_index_context *)p_vcontext;
char **entry1 = (char **)ventry1;
char *s1 = entry1[p_context->sortColumnIndex];
char **entry2 = (char **)ventry2;
char *s2 = entry2[p_context->sortColumnIndex];
return strcmp(s1, s2);
}
void sort_on_index(char **objs, size_t nRows, size_t nColumns, size_t sortColumnIndex)
{
struct sort_on_index_context context;
if (sortColumnIndex < 0 || sortColumnIndex >= nColumns)
return; /* Print an error or throw an exception! */
context.nColumns = nColumns;
context.sortColumnIndex = sortColumnIndex;
context.nRows = nRows;
qsort_s(objs, nRows, sizeof(char *) * nColumns, compare, (void *)&context);
}
We pass sizeof(char *) * nColumns because we want qsort_s to treat each contiguous group of nColumns char pointers as single blocks to be rearranged in order.
And then you would call it something like this:
char *objs[50][3];
size_t nRows = sizeof(objs)/sizeof(objs[0]);
size_t nColumns = sizeof(objs[0])/sizeof(objs[0][0]);
size_t column_id_to_sort = 1; /* or whatever you want to define as your sort key. */
/* Fill up your "objs" array however you like */
/* Now do the sort: */
sort_on_index(&objs[0][0], nRows, nColumns, column_id_to_sort);
Edit
If qsort_s or some equivalent is not available in your development environment, you may need to communicate the necessary information to your sort function via a static variable, e.g.
static struct sort_on_index_context context;
static int compare(const void *ventry1, const void *ventry2)
{
char **entry1 = (char **)ventry1;
char *s1 = entry1[context.sortColumnIndex];
char **entry2 = (char **)ventry2;
char *s2 = entry1[context.sortColumnIndex];
return strcmp(s1, s2);
}
Update
You can extend the method to supply your own custom comparison method like so:
struct sort_on_index_context_custom
{
size_t nRows;
size_t nColumns;
size_t sortColumnIndex;
int (*comparer)(const char *, const char *);
};
static int compare_custom(void *p_vcontext, const void *ventry1, const void *ventry2)
{
struct sort_on_index_context_custom *p_context = (struct sort_on_index_context_custom *)p_vcontext;
char **entry1 = (char **)ventry1;
char *s1 = entry1[p_context->sortColumnIndex];
char **entry2 = (char **)ventry2;
char *s2 = entry2[p_context->sortColumnIndex];
return p_context->comparer(s1, s2);
}
void sort_on_index_custom(char **objs, size_t nRows, size_t nColumns, size_t sortColumnIndex, int (*comparer)(const char *, const char *))
{
struct sort_on_index_context_custom context;
if (sortColumnIndex < 0 || sortColumnIndex >= nColumns)
return; /* Print an error or throw an exception! */
context.nColumns = nColumns;
context.sortColumnIndex = sortColumnIndex;
context.nRows = nRows;
context.comparer = comparer;
qsort_s(objs, nRows, sizeof(char *) * nColumns, compare_custom, (void *)&context);
}
And then call it like this to sort a given column of strings as integers:
static int integer_compare(const char *s1, const char *s2)
{
int int1 = atoi(s1);
int int2 = atoi(s2);
return int1 - int2;
}
sort_on_index_custom(&objs[0][0], nRows, nColumns, 1, integer_compare);

Code for parsing a character buffer

I want to parse a character buffer and store it in a data structure.
The 1st 4 bytes of the buffer specifies the name, the 2nd four bytes specifies the length (n) of the value and the next n bytes specifies the value.
eg: char *buff = "aaaa0006francebbbb0005swisscccc0013unitedkingdom"
I want to extract the name and the value from the buffer and store it a data structure.
eg: char *name = "aaaa"
char *value = "france"
char *name = "bbbb"
char *value = "swiss"
After storing, I should be able to access the value from the data structure by using the name.
What data structure should I use?
EDIT (from comment):
I tried the following:
struct sample {
char string[4];
int length[4];
char *value; };
struct sample s[100];
while ( *buf ) {
memcpy(s[i].string, buf, 4);
memcpy(s[i].length, buf+4, 4);
memcpy(s[i].value, buf+8, s.length);
buf += (8+s.length);
}
Should I call memcpy thrice? Is there a way to do it by calling memcpy only once?
How about not using memcpy at all?
typedef struct sample {
char name[4];
union
{
char length_data[4];
unsigned int length;
};
char value[];
} sample_t;
const char * sample_data = "aaaa\6\0\0\0francebbbb\5\0\0\0swisscccc\15\0\0\0unitedkingdom";
void main()
{
sample_t * s[10];
const char * current = sample_data;
int i = 0;
while (*current)
{
s[i] = (sample_t *) current;
current += (s[i])->length + 8;
i++;
}
// Here, s[0], s[1] and s[2] should be set properly
return;
}
Now, you never specify clearly whether the 4 bytes representing the length contain the string representation or the actual binary data; if it's four characters that needs to run through atoi() or similar then you need to do some post-processing like
s[i]->length = atoi(s[i]->length_data)
before the struct is usable, which in turn means that the source data must be writeable and probably copied locally. But even then you should be able to copy the whole input buffer at once instead of chopping it up.
Also, please note that this relies on anything using this struct honors the length field rather than treating the value field as a null-terminated string.
Finally, using binary integer data like this is obviously architecture-dependent with all the implications that follows.
To expand on your newly provided info, this will work better:
struct sample {
char string[4];
int length;
char *value; };
struct sample s[100];
while ( *buf && i < 100) {
memcpy(s[i].string, buf, 4);
s[i].length = atoi(buf+4);
s[i].value = malloc(s[i].length);
if (s[i].value)
{
memcpy(s[i].value, buf+8, s[i].length);
}
buf += (8+s[i].length);
i++;
}
I would do something like that:
I will define a variable length structure, like this:
typedef struct {
char string[4];
int length[4];
char value[0] } sample;
now , while parsing, read the string and length into temporary variables.
then, allocate enough memory for the structure.
uint32_t string = * ( ( uint32_t * ) buffer );
uint32_t length = * ( ( uint32_t * ) buffer + 4);
sample * = malloc(sizeof(sample) + length);
// Check here for malloc errors...
* ( (uint32_t *) sample->string) = string;
* ( (uint32_t *) sample->length) = length;
memcpy(sample->value, ( buffer + 8 ), length);
This approach, keeps the entire context of the buffer in one continuous memory structure.
I use it all the time.

Quicksorting an array of pointers to structs

I'm currently attempting to use the built-in quicksort provided by C in order to sort an array of pointers to structs. I want to sort each element based on a name element within the struct.
Although my debug output of the entire array each time through the comparison function shows me that the function is indeed shifting elements, the end result is not the correct sorted order. Is there something I'm just not seeing here?
typedef struct // The custom data type.
{
char *name;
} Person;
----------------------------
Person **people; // A dynamically allocated array of Person pointers.
int numPeople; // The logical index of people.
int maxPeople; // The current maximum capacity of people.
int compare(const void *a, const void *b) // The comparison function for determining
{ // alphabetic ordering.
const Person *const *p1 = a;
const Person *const *p2 = b;
return strcmp((*p1)->name, (*p2)->name); // Compare alphabetically, return result.
}
void SomeFunction(void)
{
qsort(people, numPeople, sizeof(Person *), compare); // Perform the sort.
}
Thanks for help with this.
I have tested your code and it looks working OK. Here is the code I compiled with gcc 4.5.2:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
typedef struct // The custom data type.
{
char *name;
} Person;
Person **people; // A dynamically allocated array of Person pointers.
int numPeople; // The logical index of people.
int maxPeople; // The current maximum capacity of people.
int compare(const void *a, const void *b) // The comparison function for determining
{ // alphabetic ordering.
const Person *const *p1 = a;
const Person *const *p2 = b;
return strcmp((*p1)->name, (*p2)->name); // Compare alphabetically, return result.
}
void SomeFunction(void)
{
qsort(people, numPeople, sizeof(Person *), compare); // Perform the sort.
}
int main()
{
int iCnt;
maxPeople = 4;
numPeople = 4;
people = calloc(1, sizeof(Person *) * maxPeople);
people[0] = calloc(1, sizeof(Person));
people[1] = calloc(1, sizeof(Person));
people[2] = calloc(1, sizeof(Person));
people[3] = calloc(1, sizeof(Person));
people[0]->name = strdup("Tanya");
people[1]->name = strdup("Alfred");
people[2]->name = strdup("Harry");
people[3]->name = strdup("Oakley");
for(iCnt = 0; iCnt < numPeople; iCnt ++)
printf("[%d] %s\n", iCnt, people[iCnt]->name);
SomeFunction();
for(iCnt = 0; iCnt < numPeople; iCnt ++)
printf("[%d] %s\n", iCnt, people[iCnt]->name);
return 0;
}
The code looks legit and I'm not sure what's wrong. Could you try compiling the code I tested and see if it works?
Can you try with this
int compare(const void *a, const void *b) // The comparison function for determining
{ // alphabetic ordering.
const Person *p1 = *(const Person**)a;
const Person *p2 = *(const Person**)b;
return strcmp((p1)->name, (p2)->name); // Compare alphabetically, return result.
}

Resources