9
\$\begingroup\$

Challenge

Conceal a binary code within a word.

Specifications

  1. The first argument is a path to a file.
  2. The file contains multiple lines.
  3. Each line is a test case represented by a space separated word and a binary code.
  4. For each test case, print the binary masked word.
  5. The mask alters the word according to the following:
    • If the bit is 1, change the letter to upper case.
    • If the bit is 0, leave the letter as is.

Constraints

  1. Words are from 1 to 20 letters long.
  2. The length of each word is equal to the length of the binary code.
  3. Words are always in lower case.
  4. The number of test cases is 40.

Sample Input

hello 11001
world 10000
cba 111

Sample Output

HEllO
World
CBA

Source

My Solution

#include <stdio.h>
#include <ctype.h>

#define LINE_BUFFER 42

int main(int argc, char *args[]) {
    if (argc < 2) {
        puts("File path not provided.");
        return 1;
    }

    if (argc > 2) {
        puts("Excessive arguments, only the first will be considered.");
    }

    FILE *file = fopen(args[1], "r");
    if (file == NULL) {
        puts("Could not access file / file not found.");
        return 1;
    }

    char line[LINE_BUFFER];
    while (fgets(line, LINE_BUFFER, file)) {
        for (int i = 0, word_length = 0; ; i++, word_length++) {
            if (line[i] == ' ') {
                line[i++] = '\0';
                for (int j = 0; j < word_length; j++, i++) {
                    if (line[i] == '1') {
                        line[j] = toupper(line[j]);
                    }
                }
                printf("%s\n", line);
                break;
            }
        }
    }

    fclose(file);
}
\$\endgroup\$

2 Answers 2

10
\$\begingroup\$

Handling arguments and errors

In C, args is conventionally called argv.

Do not contaminate stdout with error messages, which should go to stderr instead. One way to write error messages to stderr is using fprintf(stderr, …). However, a more idiomatic and informative way is using perror(). For example, perror(args[1]) might print an error message like blah.txt: No such file or directory.

An even friendlier solution is to interpret the spec in such a way that there aren't so many possible error conditions. For example, if there are no files specified you could just read from stdin, and if there are multiple files specified you could process them all.

Code structure

I'm not a fan of the whileforifforif nesting, especially since the body of if (line[i] == ' ') only runs once per line. I think that the code would be tidier if you searched for the space delimiter using strchr(). If you are on a system where strsep() is available (such as BSD or GNU), use that instead. Otherwise, strtok(), strtok_r(), or strtok_s() could also work.

You don't need printf("%s", …) when puts(…) will do.

Suggested solution

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

#define BUFFER_SIZE 42

int toggle_case(FILE *file) {
    char line[BUFFER_SIZE], *word, *mask;
    while (fgets(line, BUFFER_SIZE, file)) {
        if (!(mask = strchr(line, ' '))) {
            fprintf(stderr, "Invalid input: %s\n", line);
            return -1;
        }

        for (*mask++ = '\0', word = line; *word && *mask; mask++, word++) {
            if (*mask == '1') {
                *word = toupper(*word);
            }
        }
        puts(line);
    }
    return 0;
}

int main(int argc, char *argv[]) {
    FILE *file = stdin;
    if (argc <= 1) {
        toggle_case(stdin);
    } else for (int i = 1; i < argc; i++) {
        if (NULL == (file = fopen(argv[i], "r"))) {
            perror(argv[i]);
            return EXIT_FAILURE;
        }
        int err = toggle_case(file);
        fclose(file);
        if (err) return EXIT_FAILURE;
    }
}
\$\endgroup\$
1
  • 1
    \$\begingroup\$ I agree with everything you said – apart from putting *mask++ = '\0' into the for-loop :) \$\endgroup\$
    – Martin R
    Sep 6, 2016 at 6:00
11
\$\begingroup\$
  • 42

    #define LINE_BUFFER 42
    

    42 is not a buffer. Besides being a meaning of life, the universe, and everything, it is size. Consider renaming it to LINE_BUFFER_LENGTH.

  • No naked loops

    Every loop represents some algorithm, and therefore deservers a name. Consider

    while (fgets(line, LINE_BUFFER, file)) {
        char * mask = find_separator(line);
        *mask++ = '\0';
        convert_word_according_to_mask(line, mask);
        printf("%s\n", line);
    }
    
  • Errors (like fopen returning NULL) are better handled with perror() or strerror(errno). Both functions precisely describe what went wrong.

\$\endgroup\$
2
  • \$\begingroup\$ Hm. So like this? \$\endgroup\$
    – Legato
    Sep 7, 2016 at 3:02
  • \$\begingroup\$ @Legato Looks right, but please sep a rator \$\endgroup\$
    – vnp
    Sep 7, 2016 at 3:50

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

Not the answer you're looking for? Browse other questions tagged or ask your own question.