I'm trying to print 01-99 in words and I am somewhat successful.
Here is the source code:
#include <stdio.h>
#include <string.h>
int main(int argc, char *argv[]) {
char a, b;
char *digit1;
char *digit2;
// get digit character by character
scanf("%c%c", &a, &b);
switch(a) {
case '1':
switch(b) {
case '0':
digit1 = "ten";
break;
case '1':
digit1 = "eleven";
break;
case '2':
digit1 = "twelve";
break;
case '3':
digit1 = "thirteen";
break;
case '4':
digit1 = "fourteen";
break;
case '5':
digit1 = "fifteen";
break;
case '6':
digit1 = "sixteen";
break;
case '7':
digit1 = "seventeen";
break;
case '8':
digit1 = "eighteen";
break;
case '9':
digit1 = "nineteen";
break;
default:
digit1 = "";
break;
}
break;
case '2':
digit1 = "twenty-";
break;
case '3':
digit1 = "thirty-";
break;
case '4':
digit1 = "forty-";
break;
case '5':
digit1 = "fifty-";
break;
case '6':
digit1 = "sixty-";
break;
case '7':
digit1 = "seventy-";
break;
case '8':
digit1 = "eighty-";
break;
case '9':
digit1 = "ninty-";
break;
default:
digit1 = "";
}
switch(b) {
case '1':
digit2 = "one";
break;
case '2':
digit2 = "two";
break;
case '3':
digit2 = "three";
break;
case '4':
digit2 = "four";
break;
case '5':
digit2 = "five";
break;
case '6':
digit2 = "six";
break;
case '7':
digit2 = "seven";
break;
case '8':
digit2 = "eight";
break;
case '9':
digit2 = "nine";
break;
case '0':
digit2 = "\b";
break;
default:
digit2 = strcpy(digit1, "\b");
}
if (a != 1) {
printf("%s%s\n", digit1, digit2);
}
else {
printf("%s\n", digit1);
}
return 0;
}
I'm successful in printing from 20-99 until now. But there is an error. If I enter any of 20, 30, 40... - is not removed as it should be cause I used \b to remove that.
You could replace
digit2 = "\b";
by
if (digit1[0] != '\0')
digit1[strlen(digit1) - 1] = '\0';
which effectively removes the trailing character from digit1.
Since this works completely on the level of your strings, you avoid relying on device-specific behaviour as \b would do.
This is a little outside of the C standard as it concerns the behavior of the input/output environment, but usually the backspace character just repositions the cursor. To back up and erase, try the sequence BACKSPACE SPACE BACKSPACE.
This is the behavior of the DEC VT100 terminal, which is pretty ubiquitous (also called ANSI terminal, same is used by XTERM-derived terminals). As #jxh points out, adding terminal-specific sequences is only appropriate if the output device is a terminal. If you're writing to a file, this will not erase, but simply add these additional bytes to the output stream. However, these delayed sequences would still be interpreted correctly if written to the terminal at a later time, perhaps using cat. You could use the POSIX function isatty(3) to get a good guess at whether these sequences will be interpreted.
It may be possible to unwrite a character in a buffered write by adjusting the file position.
FILE *myfile = /*initialization*/;
/* ... */
fsetpos (myfile, -1, SEEK_CUR);
Please also consider MvG's valuable comments for more ways these methods may fail. (It really is shaky territory, being outside of the standard. brrrrr. :)
The escape code "\b" emits a terminal-control character; that is, it writes an additional control character to the output stream, it does not remove previous characters from the output stream: On most systems "hellO\bo" produces a string literal of 8 bytes equivalent to { 'h', 'e', 'l', 'l', 'O', '\b', 'o', '\0' };
It is important, though, to remember that this is a terminal-control character. What it does is terminal dependent: some terminals move the cursor left stopping at column 0, some move the cursor left with wrap, some erase the character the cursor arrives at, others don't.
But further more, if you are viewing the data other than through a terminal, it is just a byte. E.g. the code
#include <stdio.h>
int main(int argc, char* argv[]) {
printf("Hello!\b\n");
}
will generate a sequence of ascii values that, if viewed with linux "cat" might display "Hello", with Windows/DOS "more" command, "Hello!" (DOS is a non-destructive backspace) and if loaded in notepad produce "Hello!" followed by what looks like a wing-ding.
Don't confuse terminal control with io-stream control.
Related
I have already a function that convert hex char(input) to binary char(output). it works perfect, for small amount of data(input length).But when the input is too big, it stuck/not working. May be strcat take too much time. Is there some alternate solution, So i can convert big hex input characters into equivalent binary.
My function is:
void fun_hex_ch_2bin(int len_hex_str,uint8_t *hex,uint8_t *bin){
/* Extract first digit and find binary of each hex digit */
int i=0,j=0;
char array_hex[16]={'0','1','2','3','4','5','6','7','8','9','a','b','c','d','e','f'};
uint8_t *new_hex=malloc(len_hex_str*2);
char hex_char1,hex_char2;
j=0;
for(i=0;i<len_hex_str;i++)
{
hex_char1=array_hex[hex[i]&0x0f];
hex_char2=array_hex[(hex[i]>>4)&0x0f];
//printf("%c %c\n",hex_char1,hex_char2);
new_hex[j]=hex_char2;
new_hex[j+1]=hex_char1;
j=j+2;
}
for(i=0; i<len_hex_str*2; i++)
{
switch(new_hex[i])
{
case '0':
strcat(bin, "0000");
break;
case '1':
strcat(bin, "0001");
break;
case '2':
strcat(bin, "0010");
break;
case '3':
strcat(bin, "0011");
break;
case '4':
strcat(bin, "0100");
break;
case '5':
strcat(bin, "0101");
break;
case '6':
strcat(bin, "0110");
break;
case '7':
strcat(bin, "0111");
break;
case '8':
strcat(bin, "1000");
break;
case '9':
strcat(bin, "1001");
break;
case 'a':
case 'A':
strcat(bin, "1010");
break;
case 'b':
case 'B':
strcat(bin, "1011");
break;
case 'c':
case 'C':
strcat(bin, "1100");
break;
case 'd':
case 'D':
strcat(bin, "1101");
break;
case 'e':
case 'E':
strcat(bin, "1110");
break;
case 'f':
case 'F':
strcat(bin, "1111");
break;
default:
printf("Invalid hexadecimal input.");
}
}
}
Just use sprintf() instead of strcat()
char *bin; // points to a long enough buffer
int binlen = 0;
binlen += sprintf(bin + binlen, "something"); // strcat(bin, "something");
binlen += sprintf(bin + binlen, "otherthing"); // strcat(bin, "otherthing");
binlen += sprintf(bin + binlen, "foobar"); // strcat(bin, "foobar");
//...
// you can even do
binlen += sprintf(bin + binlen, "%.2f", 2.71828); // strcat(bin, "2.72");
16 is a power of 2 so converting it to binary is pretty simple.
Each hex digit corresponds to exactly 4 binary digits - and you can rely on this fact.
As a first step you need to convert the input characters from ASCII to numeric values.
That's easily done in one pass over the input hex string and subtracting 48 if the character is between '0' and '9' or subtracting 88 if the character is between 'a' and 'f' (take a look at the ASCII table if need an explanation on why).
After that the conversion is straightforward - go over the hex array and for each hex value take a look at the last bit and proceed to the next bit exactly 4 times, move to the next hex value and repeat the procedure.
Something like this:
int i = 0, j = 0;
while(i < len_hex_str)
{
bin[j++]=hex[i] & 1;
hex[i] >>= 1;
if(j % 4 == 0) i++;
}
And since you seem to be in need to have it ASCII representation, just pass over the output string and add 48 to each digit.
How can i speed up hexadecimal characters conversion to binary characters (?)
May be strcat take too much time.
Yes. Each call to strcat() takes longer and longer time as code does not take advantage of data already converted.
strcat() take n time to traverse the first characters.
1st strcat call, n = 0
2st strcat call, n = 8
3rd strcat call, n = 16
4th strcat call, n = 24
ith strcat call, n = 8*(i-1)
See how the sum (0+8+16+24+...) goes up by order of i*i as i increases?
Note that the first call to strcat(bin, ...) is suspect as bin[0] is not certainly a null character - something required when concatenating to a string.
Is there some alternate solution (?)
I recommend a re-write. Directly read from hex as binary and skip the in-between conversion to hexadecimal.
void fun_hex_ch_2bin(int len_hex_str, uint8_t *hex, uint8_t *bin) {
while (len_hex_str > 0) {
len_hex_str--;
// Start with the MSBit
for (uint8_t mask = 0x80; mask; mask >>=1) {
*bin++ = mask & *hex ? '1' : '0';
}
hex++;
}
// Append a null character as `bin` is to point to a _string_.
*bin = '\0';
}
I'd expect bin, as a string to be char* and not unit8_t *.
Part of my project, where we have to take an input file with hex numbers and convert them to MIPS code, I want to convert the hex into binary so it'd be easier for me to convert it into MIPS. However, when I run the code, it crashes and quits when it reaches the part where it calls the converter function. GDB says its a critical error c0000374. How do I fix this?
I have tried giving the target string more space and it doesn't seem to have any effect. I have also tried using malloc to no avail.
char* convertBinary (int hex)
{
char* hexdec = calloc(9, sizeof(char));
char* bin = calloc(SIZE+1, sizeof(char));
snprintf(hexdec, SIZE, "%08X", hex);
long int i;
for (i = 0; hexdec[i]; ++i)
{
switch (hexdec[i])
{
case '0':
strcat(bin, "0000");
break;
case '1':
strcat(bin, "0001");
break;
case '2':
strcat(bin, "0010");
break;
case '3':
strcat(bin, "0011");
break;
case '4':
strcat(bin, "0100");
break;
case '5':
strcat(bin, "0101");
break;
case '6':
strcat(bin, "0110");
break;
case '7':
strcat(bin, "0111");
break;
case '8':
strcat(bin, "1000");
break;
case '9':
strcat(bin, "1001");
break;
case 'A':
case 'a':
strcat(bin, "1010");
break;
case 'B':
case 'b':
strcat(bin, "1011");
break;
case 'C':
case 'c':
strcat(bin, "1100");
break;
case 'D':
case 'd':
strcat(bin, "1101");
break;
case 'E':
case 'e':
strcat(bin, "1110");
break;
case 'F':
case 'f':
strcat(bin, "1111");
break;
default:
printf("\nInvalid hexadecimal digit %c",
hexdec[i]);
}
}
return bin;
}
Also, in case it helps, here is the main function where I call this function
int main ()
{
int command = 10010100; //This is in hex
char* binaryString = convertBinary(command);
printf("The coverted binary is: %s\n", binaryString);
}
I expect the function to return a string of the binary numbers that have been converted from an 8 digit hex number. However, the program just quits and doesn't output anything. When debugged with GDB, it lays out a warning saying,
warning: Critical error detected c0000374
There are multiple problems in your code:
You do not check the for memory allocation failure.
Since you allocate 9 bytes for hexdec, snprintf(hexdec, SIZE, "%08X", hex); should be
snprintf(hexdec, 9, "%08X", hex);
The definition of SIZE is missing, as well as the #include lines. Post the complete source of the program exhibiting the offending behavior.
There is no need to loop until the end of the string hexdec: since you convert the hex value with %08X, just loop with:
for (i = 0; i < 8; ++i)
You should free(hexdec) before leaving the convertBinary function.
The code and comment do not agree in int command = 10010100; //This is in hex, which one is wrong? Probably both.
There is no need to use long type for i, int will suffice. Conversely, the argument hex should have unsigned int type.
Here is a simplified version of your code:
#include <stdio.h>
#include <stdlib.h>
char *convertBinary(unsigned int hex) {
char *bin = calloc(33, 1);
int i;
if (bin) {
for (i = 32; i-- > 0;) {
bin[i] = '0' + (hex & 1);
hex >>= 1;
}
}
return bin;
}
int main() {
int command = 0x10010100; //This is in hex
char *binaryString = convertBinary(command);
if (binaryString == NULL) {
printf("Memory allocation failure\n");
} else {
printf("The converted binary is: %s\n", binaryString);
free(binaryString);
}
return 0;
}
I'm doing an exercise from KNKings book "C Programming: A modern approach" which involves converting a phone number in alphabetic form, entered by the user, into numeric form. When the program encounters non-alphabetic characters (digits or punctuations, for example), it should leave them unchanged. I may assume that the user only enters upper-case letters.
However, my program seems to produce garbage, to say the least.
#include <stdio.h>
#define MAX_SIZE 50
int main(void)
{
char alphabetic[MAX_SIZE], ch;
int num_elements = 0;
printf("Enter phone number: ");
int i;
for (i = 0; i < MAX_SIZE && ((ch = getchar()) != '\n'); i++){
alphabetic[i] = ch;
num_elements++;
}
for (i = 0; i <= num_elements; i++){
switch (alphabetic[i]){
case 'A': case 'B': case 'C': alphabetic[i] = '2'; break;
case 'D': case 'E': case 'F': alphabetic[i] = '3'; break;
case 'G': case 'H': case 'I': alphabetic[i] = '4'; break;
case 'J': case 'K': case 'L': alphabetic[i] = '5'; break;
case 'M': case 'N': case 'O': alphabetic[i] = '6'; break;
case 'P': case 'R': case 'S': alphabetic[i] = '7'; break;
case 'T': case 'U': case 'V': alphabetic[i] = '8'; break;
case 'W': case 'X': case 'Y': alphabetic[i] = '9'; break;
default: break;
}
}
printf("%s\n", alphabetic);
return 0;
}
In particular, I enter: COLLECT-800.
It outputs something like this: u░#■ ║k ╩
What did I do wrong?
You have the right idea, but there are two things missing in your program:
Most importantly, the null terminator at the end of the string. After your for loop in which you read the number, add the line:
alphabetic[i] = '\0';
If the user enters lowercase letters, they are ignored in the switch statement. To get around this, include <ctype.h> and change the switch quantity from alphabetic[i] to toupper(alphabetic[i]). Calling toupper on an already upper case letter is benign.
You're not putting a null-terminator anywhere so it's undefined behavior when you read the string regardless of whether you modified it afterwards. Put this line:
alphabetic[num_elements] = 0;
After your for (i = 0; i < MAX_SIZE &&... loop.
Personally, I wouldn't do the getchar loop and instead read in the string like this:
scanf("%49s", alphabetic); // reads in a string up to 50 characters
for (i = 0; alphabetic[i]; i++) { ...
I am having an impossible time trying to make this work. It is just a sample run of the code without the full options.
What is needed from the program is for the user to enter there choice, 1-3 or a-c. I am using a string in case the user enters more than just a single character. The switch case then should compare just the first char in the array to the cases. And the do while loop is to make sure it keeps going until they enter the right characters.
#include <stdio.h>
#define SIZE 81
void main(){
char thing[SIZE] = {3};
int rvalue;
do
{
scanf_s("%s", thing);
switch (thing[0])
{
case 'a':
case 1:
printf("first\n");
rvalue = 1;
break;
case 'b':
case 2:
printf("second\n");
rvalue = 2;
break;
case 'c':
case 3:
printf("third\n");
rvalue = 3;
break;
default:
printf("Wrong\n");
rvalue = 4;
break;
}
} while (rvalue == 4);
}
Change
scanf_s("%s", thing);
To
scanf_s("%s", thing,(unsigned int)sizeof(thing)); //Read the comments to know why the cast is required
This done because scanf and scanf_s are different function. scanf_s has an additional argument present to prevent buffer overflows.
Also change these
case 1:
case 2:
case 3:
To
case '1':
case '2':
case '3':
Because the character 1('1') and the rest of them are different from the integer 1. The characters(those enclosed in single quotes) have their values represented in the ASCII table.
As far as it seems, you want to print first when the first character in the thing string is a or 1, and so on.
The problem is that case 1: is not same as case '1':. 1 is an int, '1' is a char, and as you are comparing the first character of the string, you need to change your casestatements a bit.
Code:
#include <stdio.h>
#define SIZE 81
void main(){
char thing[SIZE] = {3};
int rvalue;
do
{
scanf_s("%s", thing,SIZE);
switch (thing[0])
{
case 'a':
case '1':
printf("first\n");
rvalue = 1;
break;
case 'b':
case '2':
printf("second\n");
rvalue = 2;
break;
case 'c':
case '3':
printf("third\n");
rvalue = 3;
break;
default:
printf("Wrong\n");
rvalue = 4;
break;
}
} while (rvalue == 4);
}
My lexical analyzer recognizes digits(5,555,543667), decimals(44.65,4.1), and periods(.).
I can count digits, decimals, and periods fine but when I come across a digit and period next to each other it counts it as a decimal.
Consider a text file that contains: 555 2.3 55.23 44 5.
My output would be
1 type 1: 555
2 type 3: 2.3
3 type 3: 55.23
4 type 1: 44
5 type 3: 5.
Where type 3 is my identifier for a decimal.
I would want the 5th and 6th tokens to be counted as a digit and then a period.
Here is how I am handling my switch statement.
switch(*b) {
case '0':
case '1':
case '2':
case '3':
case '4':
case '5':
case '6':
case '7':
case '8':
case '9':
digits:
t.length++;
switch(*(b + t.length)) {
case '0':
case '1':
case '2':
case '3':
case '4':
case '5':
case '6':
case '7':
case '8':
case '9':
goto digits;
case '.':
goto decimal;
break;
default:
break;
}
t.type = TOKEN_DIGITS;
t.string = (char *)calloc(t.length + 1, sizeof(char));
strncpy(t.string, b, t.length);
break;
decimal:
t.length++;
switch(*(b + t.length)) {
case '0':
case '1':
case '2':
case '3':
case '4':
case '5':
case '6':
case '7':
case '8':
case '9':
goto decimal;
break;
}
t.type = TOKEN_DECIMAL;
t.string = (char *)calloc(t.length+1,sizeof(char));
strncpy(t.string,b,t.length);
break;
Tried multiple things but I am officially stuck.
You really should be using character classification functions for this kind of excercise instead of long switch statements. Your code will be a lot simpler and you won't have to use goto at all.
For example, a number could be described with the following regular expression (added whitespace to break up the various blocks):
[-+]? [0-9]* \.? [0-9]+
This already shows the possible state transitions:
A number can (optionally) start with + or - (if you support signed numbers)
It may have 0..n digits
If the following character is not a decimal point symbol, it should be a separator, otherwise it's an invalid symbol. If it's a separator, your number is terminated.
After a decimal point there should be 1..n digits
The number is terminated when you reach the end of input or you encounter a separator
All this can be done in a handful of lines of code - just have a pointer that points to your current input character and then keep stepping forward one by one and examine each character and based on the character class, decide what to do.
Now, this particular approach doesn't handle floating point numbers using scientific notation, etc. but adding thos extras is really simple once you have the basics done.
I think this complements xxbbcc's answer.
*Very roughly * something like this.
#include <stdio.h>
#include <stdlib.h>
#include <ctype.h>
yylex() {
int c;
char *p, buf[1000];
for(c = get(); isspace(c); c = get());
if(isdigit(c)) {
p = buf;
while(isdigit(c)) {
*p++ = c;
c = get();
}
*p = 0;
if(c != '.') {
unget(c);
int i = atoi(buf);
return INT;
}
assert(c == '.');
*p++ = c;
c = get();
while(isdigit(c)) {
*p++ = c;
c = get();
}
*p = 0;
float f = atof(buf);
unget(c);
return DECIMAL;
}
}
There's a lot of details left unsaid. Watching for EOF. Buffer overflow. Setting yylval to the int or float. Parsing tokens other than simple numbers.
Use a variable like digit_follow_peroid to keep the state. Every time you encounter a peroid, set the variable to false and then when you encounter a digit in the decimal switch block, set it to true. check the variable's value to determine the t.lengthbefore strncpy. Maybe you also need other variables to work together with it. The best way is to define a state-transition matrix, which is much better than gotos.