I need to make a simple translator. For example:
input: "foo" output: "bar"
input: "the" output: "teh"
input: "what" output: "wut"
I know I can write it like this:
if (!strcmp(input, "foo"))
puts("bar");
else if (!strcmp(input, "the"))
puts("teh");
else if (!strcmp(input, "what"))
puts("wut");
But that's big and messy. Is there a shortcut to do this? I know that in PHP (sorry for the inevitable syntax errors, I'm not proficient) there's something like this:
value = array(
"foo" => "bar",
"the" => "teh",
"what" => "wut"
);
How can I shorten the original code using something like a PHP array?
You can define a struct, which contains the word and the translation:
typedef struct {
const char *word;
const char *translation;
} translate_t;
Then you can just create an array of structs like this:
const translate_t translate[] = {{"foo", "bar"},
{"the", "teh"},
{"what", "wut"}};
If you want print out the words and translations, then you can just do this:
size_t size = sizeof translate/sizeof *translate;
for (size_t i = 0; i < size; i++) {
printf("Word: %s Translation: %s\n", translate[i].word, translate[i].translation);
}
Which will output:
Word: foo Translation: bar
Word: the Translation: teh
Word: what Translation: wut
This is a good approach for associating a word with a translation.
UPDATE: #Olaf suggested using a macro for size, which is far better for declaring sizes of arrays. Therefore, the above code can be expressed as:
#define ARRAY_SIZE(x) ((sizeof x)/sizeof *x) /* near top, or before main() is a good place for this */
for (size_t i = 0; ARRAY_SIZE(translate); i++) {
printf("Word: %s Translation: %s\n", translate[i].word, translate[i].translation);
}
Found it out:
const char *Translate[] = {
"foo", "bar",
"the", "teh",
"what", "wut"
};
int t_idx(char *s)
{
int i;
for (i = 0; Translate[i]; i += 2)
if (!strcmp(Translate[i], s))
return i+1;
return -1;
}
const char *translate(char *s)
{
int idx = t_idx(s);
return (idx == -1) ? s : Translate[idx];
}
Return values of translate:
translate("what") = "wut"
translate("some") = "some"
translate("foo") = "bar"
There are no map or associative array types in standard C; you must implement it yourself. A simple idea would be to use a struct:
#include <string.h>
#include <stdio.h>
struct map {
struct map_elem {
char *key;
char *value;
} * elem;
size_t size;
};
int main(void) {
struct map_elem map_elem[] = {
{"foo", "bar"}, {"the", "teh"}, {"what", "wut"}};
struct map const map = {map_elem, sizeof map_elem / sizeof *map_elem};
char input[] = "foo";
for (size_t i = 0; i < map.size; i++) {
struct map_elem *elem = map.elem + i;
if (strcmp(input, elem->key) == 0) {
puts(elem->value);
break;
}
}
}
Of course, it's just a little example.
Related
In my code I use the iniparser (https://github.com/ndevilla/iniparser) to parse double, int and strings. However, I'm interested into parsing arrays, delimited with comma such as
arr = val1, val2, ..., valn
Any easy and quick way, like the parser above?
The best way is to make your own structure.
You can find easily on the web structures that you can import for your code. Another solution, not so great is to put your values as void pointers. And when you want to take your values back you take the void pointer and cast it with which kind of value you want( int,double,char ect). But this may conflict the values so you must be careful. You must know what kind value is in which cell of the pointer. It is not the ideal way but its a cheat way to avoid making your own structure.
You can use libconfini, which has array support.
test.conf:
arr = val1, val2, ..., valn
test.c:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <confini.h>
struct my_conf_T {
size_t arrlen;
char ** arr;
};
static int my_listnr (IniDispatch * this, void * v_conf) {
#define conf ((struct my_conf_T *) v_conf)
if (ini_string_match_si("arr", this->data, this->format)) {
conf->arrlen = ini_array_get_length(this->value, INI_COMMA, this->format);
if (!conf->arrlen || !this->v_len) {
/* Array is empty */
return 0;
}
conf->arr = (char **) malloc(conf->arrlen * sizeof(char *) + this->v_len + 1);
if (!conf->arr) {
fprintf(stderr, "malloc() failed\n");
exit(1);
}
char * remnant = (char *) ((char **) conf->arr + conf->arrlen);
memcpy(remnant, this->value, this->v_len + 1);
for (size_t idx = 0; idx < conf->arrlen; idx++) {
conf->arr[idx] = ini_array_release(&remnant, INI_COMMA, this->format);
ini_string_parse(conf->arr[idx], this->format);
}
}
return 0;
#undef conf
}
int main () {
struct my_conf_T my_conf = (struct my_conf_T) { 0 };
if (load_ini_path("test.conf", INI_DEFAULT_FORMAT, NULL, my_listnr, &my_conf)) {
fprintf(stderr, "Sorry, something went wrong :-(\n");
return 1;
}
if (my_conf.arr) {
/* Do something with `my_conf.arr`... */
for (size_t idx = 0; idx < my_conf.arrlen; idx++) {
printf("arr[%zu] = %s\n", idx, my_conf.arr[idx]);
}
free(my_conf.arr);
}
return 0;
}
Output:
arr[0] = val1
arr[1] = val2
arr[2] = ...
arr[3] = valn
P.S. I happen to be the author.
Basically, is there any way to split an array of strings into arrays of strings before and after a token ("|") in C.
An example is shown below.
char *input[] = {"hello","I","am","|","a","cool","|","guy"}
//code
and the result is 3 arrays, containing
{"Hello","I","am"}
{"a","cool"}
{"guy"}
I tried strtok but that seems to split a string into pieces, rather than an array of strings into new, separate, sub-arrays of strings. I also do not know exactly how many "|" tokens will be present, and will need an unknown amount of new arrays (safe to say it'd be less than 10). They will be passed to execvp so having it as one string and just remembering where to start and stop looking will not work.
They will be passed to execvp
Assuming the strings include the program to be executed (the 1st parameter to execvp()) and the strings will be used in the order of appearance as per this pointer-array
char *input[] = {"hello","I","am","|","a","cool","|","guy"}
then a possible simple solution without any duplications might look like this:
#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
char * input[] = {"hello", "I", "am", "|",
"a", "cool", "|",
"guy", "|"}; /* note the additional trailing `"|"`. */
int main(void)
{
char ** pcurrent = input;
char ** pend = pcurrent + sizeof input / sizeof *input;
while (pcurrent < pend)
{
{
char ** ptmp = pcurrent;
while (ptmp < pend && **ptmp != '|')
{
++ptmp;
}
*ptmp = NULL;
}
{
pid_t pid = fork();
if ((pid_t) -1) == pid)
{
perror("fork() failed");
exit(EXIT_FAILURE);
}
if ((pid_t) 0) == pid) /* child */
{
execvp(pcurrent[0], pcurrent);
perror("execvp() failed");
exit(EXIT_FAILURE);
}
/* parent */
pcurrent = ptmp + 1;
}
} /* while (pcurrent < pend) */
} /* int main(void) */
You need manually to split the input array. And dynamically allocate a new place to store result. E.g. as:
#include <stdio.h>
#include <stdbool.h>
int main()
{
char *input[] = {"hello","I","am","|","a","cool","|","guy"};
int inputLength = sizeof(input)/sizeof(input[0]);
printf("inputLength - %d\n", inputLength);
const char ***result2DimArray = malloc(sizeof(char**) * inputLength);
int *result2DimArrayLengths = malloc(sizeof(int) * inputLength);
memset(result2DimArrayLengths, 0, sizeof(int) * inputLength);
const char **currentSection = 0;
int nextSectionNumber = 0;
for(int inputIndex = 0; inputIndex < inputLength; inputIndex++)
{
if(input[inputIndex][0] == '|')
{
currentSection = 0;
}
else
{
if(!currentSection)
{
currentSection = malloc(sizeof(char*) * inputLength);
result2DimArray[nextSectionNumber] = currentSection;
nextSectionNumber++;
}
*currentSection = input[inputIndex];
currentSection++;
result2DimArrayLengths[nextSectionNumber-1]++;
}
}
/*Checking the result*/
printf("total sections - %d\n", nextSectionNumber);
for(int i=0; i<nextSectionNumber;i++)
{
for(int j=0;j<result2DimArrayLengths[i];j++)
{
printf(result2DimArray[i][j]);
printf(", ");
}
puts("");
}
return 0;
}
Here is a solution which doesn't involve dynamic memory allocation.
Before going in to the details ...
I think it's useful when tackling a problem like this to think about how the "strings" are stored in memory. It might look something like in the attached picture. (The memory addresses are completely unrealistic - and there would be null terminators at the end of each string - but you get the idea).
As the picture shows, the vital information we need for each 'sub-array' can be stored in a <char **, int> pair. The char ** is the address of the first "string" in the sub-array; the int is the number of strings it contains.
We can use a struct string_array_t to store this information.
typedef struct {
// Pointer to first string in sub-array
char **p;
// Number of strings in sub-array
int count;
} string_array_t;
We allocate an array of these on the stack; thus no need for malloc() or free() - as long as we allocate enough sub-arrays.
string_array_t string_arrays[MAX_SUB_ARRAYS] = {0};
char *input[] = {"hello", "I", "am", "|", "a", "cool", "|", "guy"};
// Pointer to current sub-array
string_array_t *cur = NULL;
size_t n_sub_arrays = 1;
Initialize our counters and pointers:
int i = 0, j = 0, k = 0;
cur = &string_arrays[0];
size_t n_strings_total = sizeof(input) / sizeof(input[0]);
Then loop over the array.
for (i = 0; i < n_strings_total; i++) {
if (!strcmp(input[i], "|")) {
// Store total number of strings in this sub-array
cur->count = k;
k = 0;
// Switch to next sub-array
cur = &string_arrays[++j];
if (j >= MAX_SUB_ARRAYS) {
fprintf(stderr, "Not enough sub-arrays allocated ...\n");
break;
}
n_sub_arrays++;
continue;
}
if (k == 0) {
cur->p = &input[i];
}
k++;
}
cur->count = k;
Print the results.
printf("Found %zu sub arrays ...\n", n_sub_arrays);
for (i = 0; i < n_sub_arrays; i++) {
string_array_t *cur = &string_arrays[i];
for (j = 0; j < cur->count; j++) {
printf("%s ", *(cur->p++));
}
printf("\n");
}
I need to translate a value into a human readable string. Normally for things I define I would use values that start at zero and create a simple array of strings with the values as the index.
static const char *foo[] = { "foo", "bar", "etc" };
if (val < 3) printf("%s\n", foo[val]);
For this circumstance I have values that do not start at zero and there are some gaps between them. Is there a good way to do this without having to manually code in a bunch of empty strings for the indexes without a matching value/string pair?
static const char *foo[] = { "", "", "", "foo", "", "bar", "", "", "etc" };
As of C99, you can use designated initialisers:
static const char *foo[] = {
[3] = "foo",
[5] = "bar",
[8] = "etc"
};
This is equivalent to the array definition you posted and will generate an array with 9 entries. There is a similar syntax for the initialisation of structs:
struct Person joe = {
.name = "Joe", .age = 24, .favcolor = "mauve"
};
Note that this is a C feature only and will not work in C++.
If there aren't too many gaps, you can encode each contiguous sequence as a separate array, and then do a little bounds-checking to find the appropriate one to use. Here's a quick-and-dirty example:
#include <stdio.h>
#include <stdlib.h>
static const int array_first_indices[] = {3, 15, 28, 32};
static const char * array0[] = {"foo"};
static const char * array1[] = {"bar", "baz"};
static const char * array2[] = {"bloop", "blorp", "blat"};
static const char * array3[] = {"glop", "slop", "bop"};
#define check_array(whichArray, idx) { \
unsigned int relIdx = idx - array_first_indices[whichArray]; \
if (relIdx < (sizeof(array##whichArray)/sizeof(const char *))) \
return array##whichArray[relIdx]; \
}
const char * LookupWord(int idx)
{
check_array(0, idx);
check_array(1, idx);
check_array(2, idx);
check_array(3, idx);
return NULL;
}
int main(int args, char ** argv)
{
for (int i=0; i<50; i++) printf(" LookupWord(%i) = %s\n", i, LookupWord(i));
return 0;
}
For a fully general lookup mechanism, you'd probably need to use a data structure like a hash table or a tree; C implementations of those data structures are available, although if you have the option of using C++ it would be easier to use those data structures in that language, as they are provided by the standard library.
Create a sorted array that maps IDs to strings and use the bsearch() function to look up the string:
#include <stdio.h>
#include <stdlib.h>
struct id_msg_map {
int id;
char const* str;
};
int comp_id_string( const void* key, const void* element)
{
int key_id = ((struct id_msg_map*) key)->id;
int element_id = ((struct id_msg_map*) element)->id;
if (key_id < element_id) return -1;
if (key_id > element_id) return 1;
return 0;
}
static struct id_msg_map msg_map[] = {
{3, "message 3"} ,
{12, "message 12"},
{100, "message 100"},
{32000, "message 32000"},
};
#define ELEMENTS_OF(x) (sizeof(x) / sizeof((x)[0]))
char const* get_msg(int x)
{
struct id_msg_map key = {x};
struct id_msg_map* msg = bsearch(&key, msg_map, ELEMENTS_OF(msg_map), sizeof(msg_map[0]), comp_id_string);
if (!msg) return "invalid msg id";
return msg->str;
}
void test_msg(int x)
{
printf("The message for ID %d: \"%s\"\n", x, get_msg(x));
}
int main(void)
{
test_msg(0);
test_msg(3);
test_msg(100);
test_msg(-12);
return 0;
}
You can use designated initialisers, as described in M. Oehm's post, but that silently introduces the same gaps you were referring to earlier (with implicit 0 values). That option is most suitable when you know 0 will never be an actual selection, when the table doesn't change dynamically (particularly in size) and when the size of the table is small.
If the table is particularly large, but items are never added or removed from it you can use qsort and bsearch on a key/value-pair style structure. For example:
struct foo_pair {
int key;
char *value;
};
int foo_pair_compare(void *x, void *y) {
struct foo_pair *a = x, *b = y;
return (a->key > b->key) - (a->key < b->key);
}
int main(void) {
struct foo_pair foo[] = { { .key = 3, .value = "foo" },
{ .key = 5, .value = "bar" },
{ .key = 6, .value = "etc" } };
/* qsort needs to be done at the start of the program,
and again each time foo changes */
qsort(foo, sizeof foo / sizeof *foo, sizeof *foo, foo_pair_compare);
/* bsearch is used to retrieve an item from the sorted array */
struct foo_pair *selection = bsearch(&(struct foo_pair) { .key = 5 },
foo, sizeof foo / sizeof *foo,
sizeof *foo, foo_pair_compare);
}
When items are routinely added or removed from the collection, it will make more sense to select a hashtable or some kind of ordered map. If you can't be bothered writing and testing your own of these collections, I imagine there are plenty of tried & tested libraries on the internet that you could check out.
#include<stdio.h>
struct test_ {
char *device_name;
char *path_name;
};
typedef struct test_ test_t;
struct capabilities_ {
test_t tab[3];
int enable;
};
static test_t table[3] = {
{ "first", "john"},
{ "second", "mike"},
{ "third:", "vik" },
};
int main()
{
struct capabilities_ cap;
//cap.tab = table; ???
return 0;
}
I have a static array with the value, which I want to assign/copy to the same type/sized variable under the structure to table to cap.tab. Could you please help how to do that?
To do it at runtime, you can use user9000's approach, or something like this:
for (i = 0; i < 3; i++)
cap.tab[i] = table[i];
Or, convert your tab to use a pointer to test_t instead of array of test_t.
struct capabilities_ {
test_t *tab;
int enable;
};
int main()
{
struct capabilities_ cap;
cap.tab = table;
printf("%s\n", cap.tab[1].device_name);
return 0;
}
Or, if you are trying to do it at initialization, use one of the following:
struct capabilities_ cap = {
{
{ "first", "john" },
{ "second", "mike" },
{ "third:", "vik" },
},
1
};
Or this,
struct capabilities_ cap = {
{
table[0],
table[1],
table[2],
},
1
};
If you want to copy the strings, and not just the pointer to the strings, you'll need to allocate memory for each string in the target capabilities struct. Here is one way to do that
for (int i = 0; i < sizeof(table) / sizeof(test_t); i++)
{
size_t device_name_length = strlen(table[i].device_name);
size_t path_name_length = strlen(table[i].path_name);
size_t target_device_length = device_name_length + 1; // + 1 for null terminator
size_t target_path_length = path_name_length + 1; // + 1 for null terminator
cap.tab[i].device_name = (char*) malloc( target_device_length );
cap.tab[i].path_name = (char*) malloc( target_path_length );
strncpy_s(cap.tab[i].device_name, target_device_length, table[i].device_name, device_name_length);
strncpy_s(cap.tab[i].path_name, target_path_length, table[i].path_name, path_name_length);
}
If you don't care to make a deep copy, you can use the shallow copy mechanism shown by user9000 to just copy the pointers to the strings.
Also, if you use the mechanism above, don't forget to free if your capabilities is going to go out of scope and no longer be used :)
You can do it like so:
memcpy(cap.tab, table, sizeof (test_t) * (sizeof(table) / sizeof(test_t)));
This is just the same mechanism used in copying a string to another. Since you have the table size known, you can just do:
memcpy(cap.tab, table, 3 * sizeof(test_t));
The equivalent method of copying characters is like:
memcpy(str, str1, sizeof(char) * 4); // copy 4 of str1 into str
Learning C and having many doubts.
I have a function (lets say function 1) that calls another function (lets say function 2).
Function 2 calculates an array of string.
How can I use this array in function 1?
Some code example:
int find_errors(char* word)
{
char error[100];
/*Given the word, It will find the duplicate chars and store it in the
error array. */
return 0;
}
int find_word(char* word)
{
find_errors (word);
printf("%s\n", error);
return 0;
}
There are at least three possible approaches:
Use a global variable
pass a parameter between them
return a pointer from the function
There are multiple ways to do this.
1) Create a dynamic array and return a pointer to the array. This will require you to manually free the memory for the array at a later time.
#define NUM_ELEMS 50
// In find_error():
char* error = malloc(NUM_ELEMS * sizeof(char));
return error;
// In find_word():
char *error = find_errors();
// do stuff
free(error);
2) Pass a pointer to find_errors that it can use as the error array. This will not require you to manually free the memory.
// In find_word():
char error[NUM_ELEMS];
find_error(error);
3) Use a global array. May make it more difficult for other people to understand your code. Has other potential problems as well.
// In global scope:
char error[NUM_ELEMS];
Your question relates to "call-by-reference" and "call-by-value".
char* getNewValsToSet(void)
{
char* new_vals = (char*) malloc(sizeof(char[5]));
new_vals[4] = '\0';
return new_vals;
}
void setValuesEven(char* vals_to_set)
{
vals_to_set[0] = 'A';
vals_to_set[2] = 'C';
}
void setValuesOdd(char* vals_to_set)
{
vals_to_set[1] = 'B';
vals_to_set[3] = 'D';
}
int main(void)
{
char* some_vals_to_set = getNewValsToSet();
setValsEven(some_vals_to_set);
setValsOdd(some_vals_to_set);
// ... now has vals "ABCD"
free(some_vals_to_set); //cleanup
return 0;
}
If you have "doubts" about learning C, IMHO it's one of the best things you can do (no matter the language in which you work) because it will explain exactly how things work "under-the-hood" (which all high-level languages try to hide to some degree).
You need to declare the error array globally and use it just like you did.
EDIT: using global variables isn't the best practice in most of the cases, like this one.
Here is an example of what you are looking for with an awesome console output. It dynamically allocates the array to hold any number errors (duplicate characters in your case) that may occur.
//Only free errors if result is > 0
int find_errors(char* word, char** errors)
{
int num_errors = 0;
int word_length = strlen(word);
int ARRAY_SIZE = MIN(8, word_length);
char existing[word_length];
int existing_index = 0;
*errors = NULL;
for(int i = 0; i < word_length; i++)
{
char character = word[i];
//Search array
for (int n = 0; n < word_length; ++n ) {
if(n >= existing_index)
{
existing[n] = character;
existing_index++;
break;
}
if (existing[n] == character) {
num_errors++;
if(!*errors)
*errors = (char*)malloc(ARRAY_SIZE * sizeof(char));
//Check if we need to resize array
if(num_errors >= ARRAY_SIZE)
{
ARRAY_SIZE *= 2;
ARRAY_SIZE = MIN(ARRAY_SIZE, word_length);
char *tmp = (char*)malloc(ARRAY_SIZE * sizeof(char));
memcpy(tmp, *errors, (unsigned long)ARRAY_SIZE);
free(*errors);
*errors = tmp;
}
//Set the error character
(*errors)[num_errors - 1] = character;
break;
}
}
}
return num_errors;
}
int find_word(char* word)
{
char* errors;
int errCount = find_errors (word, &errors);
if(errCount > 0)
{
printf("Invalid Characters: ");
for(int i =0; i < errCount; i++)
{
printf("%c ", errors[i]);
}
printf("\n");
free(errors);
}
return 0;
}
int main(int argc, char *argv[])
{
find_word("YWPEIT");
find_word("Hello World");
find_word("XxxxXXxXXoooooooOOOOOOOOOOOOOOOooooooooOOOOOOOOOOOOooooooOOO");
}