0

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?

Yu Hao
  • 119,891
  • 44
  • 235
  • 294
MD XF
  • 7,860
  • 7
  • 40
  • 71

3 Answers3

5

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);
}
RoadRunner
  • 25,803
  • 6
  • 42
  • 75
  • 1
    One addition: use the `const char *` if you point to string literals. As a sidenote to other readers: the calculation of the array length (`size = ...`) is a good candidate for a macro (I wounder why C11 die not add this, like they added `offsetof`). – too honest for this site Jan 19 '17 at 13:26
  • @Olaf added `const` and macro for `size_t size`. I appreciate the suggestions. – RoadRunner Jan 19 '17 at 15:07
  • Well, the sidenote was nothing I expected you to add for that example, but nice to see you took my comment serious:-). It was more of a general recommendation to readers. Nit-pick: `lengthof`/`length_of` (like in the Linux kernel) might be a better name; after all `size` is too close to `sizeof`/size_t`. (you already got my UV, no need to edit ;-) – too honest for this site Jan 19 '17 at 16:02
0

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"
MD XF
  • 7,860
  • 7
  • 40
  • 71
  • Where is the "trick"? And why not make the array `const`, too? – too honest for this site Jan 18 '17 at 03:06
  • 4
    Note that your code goes trampling merrily beyond the end of the array. Either define `enum { NUM_TRANSLATE = sizeof(Translate) / sizoef(Translate[0]) };` and test `i < NUM_TRANSLATE` in the loop condition, or add one (or perhaps two) NULL pointers at the end of the array. A structure is probably better, though. – Jonathan Leffler Jan 18 '17 at 04:14
  • That's not a trick, but bad coding style. As @JonathanLeffler wrote, a `struct` would be the better way. C does support different kinds of compound datatypes - use them. – too honest for this site Jan 19 '17 at 03:36
  • Why are you ignoring the posts that have addressed your question? They could be instructive. – ryyker Jan 19 '17 at 13:57
  • Feel free to think this. But be prepared to have a very serious talk with your boss if you write such code as a professional programmer. If you ask a self-answered question, you should at least be fair enough to accept another answer is better than your's and accept that one. – too honest for this site Jan 21 '17 at 03:19
  • @Redesign Being no professional programmer is quite a lame excuse for writing bad code. While there are sometimes reasons to write less readable code e.g. to enhance performance, such optimisations should not be done preliminarily. And this code is very unlikely to perform any better than more readable and safer code. – too honest for this site Jan 23 '17 at 17:14
  • @Olaf My code is much easier to expand and has no readability issues. If there's something unsafe about it I have yet to find out, so please point out how it's unsafe. – MD XF Jan 23 '17 at 17:21
0

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.

Jonathan Leffler
  • 730,956
  • 141
  • 904
  • 1,278
Stargateur
  • 24,473
  • 8
  • 65
  • 91
  • @JonathanLeffler [The stdlib.h and stddef.h header files define a datatype called size_t](http://stackoverflow.com/a/1119390/7076153), not really important here because `string.h` or `stdio.h` include it. Thank to correct my typos. – Stargateur Jan 18 '17 at 04:01
  • 2
    And so does `` and ``…so you don't need `` in this context. If you use `offsetof()` you need it; if you don't have any of the 'mighty trio' (``, ``, ``) you probably need it for `size_t` or `NULL`; if you use `ptrdiff_t`, or `wchar_t` or `max_align_t` you might need it. Usually, you don't have to worry about it, though. – Jonathan Leffler Jan 18 '17 at 04:04
  • One indirection too much. And why not writing const-correct code? – too honest for this site Jan 19 '17 at 03:41
  • @Olaf What is the indirection that is too much and why? "And why not writing const-correct code?", what do you want to be const? By the way, we are not in code review. – Stargateur Jan 19 '17 at 04:43
  • @Stargateur: Since when is providing **good** code reserved for code-review? "what do you want to be `const`" - you have non-`const` pointers to string literals. It is an allowed legacy, which should not be used since decades and definitively not to beginners. And the additional indirection is the inner `struct`. No need for nested `struct`s; use an array in the definition and calculate the `length_of` from the array. Said that: the code is not correct. – too honest for this site Jan 19 '17 at 13:21
  • @Olaf I don't agree, yes this is literal const string but in this context, it is valid. And it's a little example I said it. Plus I don't agree, with your second point. You have your style but don't downvote me because I don't follow your style, post your own answer. The code is perfectly correct and has no one undefined behavior. – Stargateur Jan 20 '17 at 01:17
  • @Stargateur: Where did I write it is not valid? I'm pretty confident I stated the opposite. Yet it is bad coding style. Sorry, I was a bit to quick reading; on second read, it seems to be valid indeed, just was a bit irritated for using the same name for variable and `struct` tag. Btw.: you should not assume someone downvoted just because he left a comment. But even if: a code-only answer is not a good answer. – too honest for this site Jan 20 '17 at 01:53