0

I am writing a program that converts user input to either ASCII values or binary values. If the string contains letters, each letter should be converted to ASCII. Strings of numbers will be converted to binary value of entire string. If both letters and numbers are entered, each letter will be converted to ASCII and the numbers can/will only be separated by letters, for example "32" will print the binary value "00100000", but "3a2" should be converted to "00000011", "97", "00000010".

The way the program is currently written, strings of numbers convert to binary perfectly. However, strings of letters add a decimal "0" to the end. The output converts each letter to its ASCII value, then converts the "0" to binary. I am unsure as to where this "0" is coming from. Additionally, strings beginning and ending with digits (for example "6j3") will print the ASCII value of j, then the binary value of "6", skipping the "3" entirely and printing the "j" before the "6". I would like to print each ASCII/binary value in the exact order of the user input.

I am posting my entire code for any necessary clarification, but I believe the issue is in the determineChars() function. I am also looking to use the char* letters and char* numbers functions to efficiently handle the appropriate data and store the final num[] and let[] arrays, but I am unsure of how to do this.

I am quite a beginner to C, so excuse the messiness. Corrections, as well as any further optimizations would be greatly appreciated.

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

#define EIGHT_BITS 255
#define SIXTEEN_BITS 65535
#define THIRTY_TWO_BITS 4294967295UL
#define SIXTY_FOUR_BITS 18446744073709551615ULL

//   defined in case of booleans
typedef enum { false, true } bool;

//  GET # OF ELEMENTS IN STRING
size_t getSize(char* input) {
    size_t size;
    size = strlen(input);
    printf("Number of Characters..... %d", size);
     //printf("\n----------------------------------");

    return size;
}

//  DETERMINE NUMBER OF BITS TO OUTPUT
int getBitLength(unsigned long long d) {
    int l;

    if (d <= EIGHT_BITS) {
        l = 8;
    }
    else if (d > EIGHT_BITS && d <= SIXTEEN_BITS) {
        l = 16;
    }
    else if (d > SIXTEEN_BITS && d <= THIRTY_TWO_BITS) {
        l = 32;
    }
    else if (d > THIRTY_TWO_BITS && d <= SIXTY_FOUR_BITS) {
        l = 64;
    }

    printf("\nBits..................... %d", l);

    return l;
}

// CONVERT INPUT TO BINARY VALUE
void convertToBinary(char* input) {
    static int b[64];
    int i, j, length, r;
    unsigned long long decimal;
    char* pEnd;

    // converts input to ull
    decimal = strtoull(input, &pEnd, 0);
     printf("\n\n---------------- %I64u ------------------", decimal);
    printf("\nULL...................... %I64u", decimal);
    length = getBitLength(decimal);

    //  creates array
    for (i = 0; i < length; i++) {
        r = decimal % 2;
        decimal /= 2;
        b[i] = r;
    }

    //  reverses array for binary value
    printf("\nBinary Value............. ");
    for (j = length - 1; j >= 0; j--) {
        printf("%d", b[j]);
    }
}

char* numbers(char* input) {
     char* num = (char*) malloc(sizeof(char) * 25);

     return num;
}

char* letters(char* input) {
     char* let = (char*) malloc(sizeof(char) * 25);

     return let;
}

void determineChars(char* input) {
     int i;
     char* num = numbers(input);
     char* let = letters(input);
     size_t inputSize = getSize(input);

     //   FOR EACH CHARACTER IN INPUT
     for (i = 0; i < inputSize; i++) {
          if (isdigit(input[i])) {
               //   stores number values from input into separate array
               num[i] = input[i];
               printf("\nNumbers: %c", num[i]);
          }

          if (!isdigit(input[i])) {
               //   stores letter values from input into separate array
               let[i] = input[i];
               printf("\nLetters: %c", let[i]);
               //   prints separator line + ASCII value
               printf("\n\n---------------- %c ------------------", let[i]);
               printf("\nASCII Value of %c......... %d", let[i], let[i]);
               //   removes char from input array
               input[i] = ' ';
          }
     }
     //   char array must consist of digits only
     convertToBinary(num);
}

int main() {
    //  GET INPUT
    char input[50];
    scanf("%s", input);
    determineChars(input);

    return 0;
}
J. Pex
  • 45
  • 3
  • 1
    Suggestion. This will exhibit undefined behavior if your input string is longer than 25 characters. You're passing `input` to both `numbers()` and `letters()`. You could use `strlen()` to get the length of the input string, and then use that to determine how much memory to allocate. Also, since [sizeof(char) == 1](https://stackoverflow.com/questions/2215445/are-there-machines-where-sizeofchar-1-or-at-least-char-bit-8) you don't need to include it in the parameter to `malloc()`. – dgnuff Aug 27 '18 at 21:13
  • 1
    Well yes, it looks like the `determineChars()` function always finishes by calling `convertToBinary(num)`, which *will* ultimately print a number. If your input did not end with a decimal digit, then it looks like that number will be 0. – John Bollinger Aug 27 '18 at 21:38
  • @JohnBollinger I’m attempting to call convertToBinary(num) only if the input contains digits, but I do not want to include the function call inside the for loop, as it will be called n times, where n = inputSize. How can I call convertToBinary(num) if and only if the input actually contains digits? – J. Pex Aug 27 '18 at 21:44
  • This is a parsing problem. You need to break up the input string into regions consisting of runs of digits and regions consisting of individual nondigits, and handle each region encountered appropriately. You don't necessarily need to accumulate the characters of these regions into arrays, but in any case, it looks like you need to call the output function for the appropriate region type each time you discover the end of a region, whether at the start of a different region or at the end of the input. And yes, that will be inside the loop. That's what the problem requires. – John Bollinger Aug 27 '18 at 21:45
  • regarding: `char* num = (char*) malloc(sizeof(char) * 25);` and similar statements: 1) the expression: `sizeof(char)` is defined in the C standard as 1. Multiplying anything by 1 has absolutely no effect. Suggest removing that expression from the parameter to `malloc()` 2) the returned type is `void*` which can be assigned to any pointer. Casting just clutters the code, making it more difficult to understand, debug, etc. 3) always check (!=NULL) the returned value to assure the operation was successful. – user3629249 Aug 28 '18 at 06:48
  • the posted code contains some memory leaks. Because it calls `malloc()` at least once, but never passes the resulting pointer to `free()` – user3629249 Aug 28 '18 at 06:51

1 Answers1

1

I would like to print each ASCII/binary value in the exact order of the user input.

In that case, you would have to restructure your code a bit. This is because if the input contains only digits you will have to print binary and alternate being chars and digits if the string contains both. I have tried to do this with the following code, cleaned it up a bit, removed the warnings and memory leaks.
See if this is what you want:

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

#define EIGHT_BITS 255
#define SIXTEEN_BITS 65535
#define THIRTY_TWO_BITS 4294967295UL
#define SIXTY_FOUR_BITS 18446744073709551615ULL

//  GET # OF ELEMENTS IN STRING
size_t getSize(char* input) {
    size_t size;
    size = strlen(input);
    printf("Number of Characters..... %d\n", size);
     //printf("\n----------------------------------");

    return size;
}

//  DETERMINE NUMBER OF BITS TO OUTPUT
int getBitLength(unsigned long long d) {
    int l;

    if (d <= EIGHT_BITS) {
        l = 8;
    }
    else if (d > EIGHT_BITS && d <= SIXTEEN_BITS) {
        l = 16;
    }
    else if (d > SIXTEEN_BITS && d <= THIRTY_TWO_BITS) {
        l = 32;
    }
    else if (d > THIRTY_TWO_BITS && d <= SIXTY_FOUR_BITS) {
        l = 64;
    }

    printf("\nBits..................... %d", l);

    return l;
}

// CONVERT INPUT TO BINARY VALUE
void convertToBinary(char* input) {
    static int b[64];
    int i, j, length, r;
    unsigned long long decimal;
    char* pEnd;

    // converts input to ull
    decimal = strtoull(input, &pEnd, 0);
    printf("\n---------------- %I64u ------------------", decimal);
    printf("\nULL...................... %I64u", decimal);
    length = getBitLength(decimal);

    //  creates array
    for (i = 0; i < length; i++) {
        r = decimal % 2;
        decimal /= 2;
        b[i] = r;
    }

    //  reverses array for binary value
    printf("\nBinary Value............. ");
    for (j = length - 1; j >= 0; j--) {
        printf("%d", b[j]);
    }
    printf("\n");
}

void determineChars(char* input) {
     int i;  
     long ret;
     char* ptr;  
     char c;
     size_t inputSize = getSize(input);

    ret = strtol(input, &ptr, 10);

    if((ret == 0) || ((strlen(ptr) != 0) && (strlen(input) != strlen(ptr))))
    {
        for (i = 0; i < inputSize; i++) {
            if (isdigit(input[i])) {
               c = input[i];
               printf("\nNumber: %c", c);
               convertToBinary(&c);
            }
            if (!isdigit(input[i])) {
               //   stores letter values from input into separate array            
               printf("\nLetter: %c", input[i]);
               //   prints separator line + ASCII value
               printf("\n---------------- %c ------------------\n", input[i]);
               printf("ASCII Value of %c......... %d\n", input[i], input[i]);
               //   removes char from input array

            }
        }      
    }
    else
       convertToBinary(input);

}

int main() {
    //  GET INPUT
    char input[50];
    scanf("%s", input);
    determineChars(input);  
}

I also tried out the test cases you mentioned in the question along with few others and it seems to work fine.

32
Number of Characters..... 2

---------------- 32 ------------------
ULL...................... 32
Bits..................... 8
Binary Value............. 00100000

3a2
Number of Characters..... 3

Number: 3
---------------- 3 ------------------
ULL...................... 3
Bits..................... 8
Binary Value............. 00000011

Letter: a
---------------- a ------------------
ASCII Value of a......... 97

Number: 2
---------------- 2 ------------------
ULL...................... 2
Bits..................... 8
Binary Value............. 00000010

6j3
Number of Characters..... 3

Number: 6
---------------- 6 ------------------
ULL...................... 6
Bits..................... 8
Binary Value............. 00000110

Letter: j
---------------- j ------------------
ASCII Value of j......... 106

Number: 3
---------------- 3 ------------------
ULL...................... 3
Bits..................... 8
Binary Value............. 00000011
P.W
  • 26,289
  • 6
  • 39
  • 76
  • Please avoid posting complete source in the answer. It's really hard to find what was actually changed and why. – user694733 Aug 28 '18 at 10:05
  • There were quite a few changes. Posting snippets would not have helped unless it was told where they were to be added/deleted/changed. That would require a lot of explanation, mentioning of line numbers etc. – P.W Aug 28 '18 at 10:09
  • 1
    Looking at the code in the diff tool, only changes in the `determineChars` function seem to be relevant to the question. Showing only this function would reduce lines from 110 to 30, and it would make easier for everyone to review your answer. – user694733 Aug 28 '18 at 10:17
  • This code works when the input consists of only digits. Unfortunately, when entering something like “c402,” the digits are still separated and converted to binary on their own, rather than “ASCII value of c” then “Binary value of 402.” – J. Pex Aug 28 '18 at 18:38
  • @J.Pex: I misunderstood the question. What you are asking can be done . You need to first separate the input into blocks of chars and digits and employ the same logic. – P.W Aug 29 '18 at 04:57