I'm trying to convert some text (character by character) to its binary representation. For some reason the print statement printf("Hold is %d or %c: ", hold, hold); is changing the output of my function and I have no idea how to explain it. Any help would be greatly appreciated. The test file is just a text file with Hello, World! inside of it.
With it:
Hold is 72 or H: 01001000
Hold is 101 or e: 01100101
Hold is 108 or l: 01101100
Hold is 108 or l: 01101100
Hold is 111 or o: 01101111
Hold is 44 or ,: 00101100
Hold is 32 or : 00100000
Hold is 87 or W: 01010111
Hold is 111 or o: 01101111
Hold is 114 or r: 01110010
Hold is 108 or l: 01101100
Hold is 100 or d: 01100100
Hold is 33 or !: 00100001
Without it:
1000 �
0101 �
1100 �
1100 �
1111 �
1100 �
0000 �
0111 �
1111 �
0010 �
1100 �
0100 �
0001 �
Code
#include <stdio.h>
#include <string.h>
void decimal_to_binary(unsigned long num, FILE *out) {
int i = 255, a = 0;
char binarr[255];
for (i = 0; i < 255; i++) { binarr[i] = '0'; }
if (num != 0) {
while (num != 0) {
if (num % 2 == 0) {
binarr[i] = '0';
i--;
} else {
binarr[i] = '1';
i--;
}
num /= 2;
}
} else {
fprintf(out, "00000000");
}
fprintf(out, "%s ", binarr + strlen(binarr) - 8);
printf("%s\n", binarr + strlen(binarr) - 8);
memset(binarr, 0, sizeof(binarr));
}
int main(int argc, char *argv[]) {
int hold;
FILE *in = fopen(argv[1], "r");
FILE *out = fopen(argv[2], "w+");
while (!feof(in)) {
hold = fgetc(in);
if (hold > 0 && hold != 10){
printf("Hold is %d or %c: ", hold, hold);
decimal_to_binary(hold, out);
}
}
fclose(in);
fclose(out);
return 0;
}
Your decimal_to_binary function is incorrect:
you index beyond the end of the binarr array.
you do not null terminate this array to pass it to printf.
Here is a simpler and corrected version:
void decimal_to_binary(unsigned long num, FILE *out) {
int i = 256, a = 0;
char binarr[257];
memset(binarr, '0', sizeof(binarr) - 1);
binarr[i] = '\0';
while (num != 0) {
--i;
if (num % 2) {
binarr[i] = '1';
}
num /= 2;
}
if (i > 256 - 8) // print at least 8 bits
i = 256 - 8;
fprintf(out, "%s ", binarr + i);
printf("%s\n", binarr + i);
}
Your function main has problems too:
you test for end of file with feof(in). This is incorrect, you should instead check if hold is EOF.
hard coding the value of '\n' as 10 is bad practice.
Here is a correct version:
int main(int argc, char *argv[]) {
int hold;
FILE *in = fopen(argv[1], "r");
FILE *out = fopen(argv[2], "w+");
while ((hold = fgetc(in)) != EOF) {
if (hold != '\n') {
printf("Hold is %d or %c: ", hold, hold);
decimal_to_binary(hold, out);
}
}
fclose(in);
fclose(out);
return 0;
}
I decreased the extremely large array, made sure to terminate the string with a null character, zeroed the array, then printed it using fprintf. This solved the issue.
void decimal_to_binary(unsigned long num, FILE *out){
int i = 7, a = 0;
char binarr[9];
binarr[8]='\0';
for (a=7; a>=0; a--){ binarr[a] = '0'; }
if (num != 0) {
while (num!=0){
if (num%2 == 0){
binarr[i] = '0';
i--;
}
else { binarr[i] = '1'; i--; }
num /= 2;
}
} else { fprintf(out, "00000000"); }
fprintf(out, "%s ", binarr);
memset(binarr, 0, sizeof(binarr));
}
Your program has undefined behavior for couple of reasons.
You don't have a null terminated string. Calling strlen on such a string is cause for undefined behavior.
You are modifying binarr using an out of bounds index. That is also cause for undefined behavior.
I have my annotations to your function decimal_to_binary that point out where those errors are.
void decimal_to_binary(unsigned long num, FILE *out){
int i = 255, a = 0;
char binarr[255];
for (i=0; i<255; i++){ binarr[i] = '0'; }
// All the elements of binarr are set to '0'.
// It's not a null terminated string.
if (num != 0) {
while (num!=0){
// The value of i is 255 when this loop is
// entered the first time.
// Setting the value of binarr[255] is cause for
// undefined behavior.
if (num%2 == 0){
binarr[i] = '0';
i--;
}
else { binarr[i] = '1'; i--; }
num /= 2;
}
} else { fprintf(out, "00000000"); }
fprintf(out, "%s ", binarr + strlen(binarr) - 8);
printf("%s\n", binarr + strlen(binarr) - 8);
memset(binarr, 0, sizeof(binarr));
}
The fixes are simple.
Terminate string with the null character.
for (i=0; i<255; i++){ binarr[i] = '0'; }
i--;
binarr[i] = '\0';
Use the right index when modifying binarr in the while loop.
while (num!=0){
// Decrement the index before you assign to the next element.
// When the loop is entered the first time, i = 254, which
// is used to null terminate binarray.
// The next '1' or '0' needs to be placed at i = 253.
i--;
if (num%2 == 0){
binarr[i] = '0';
}
else {
binarr[i] = '1';
}
num /= 2;
}
Related
This is an assignment for one of my classes, that requires you to convert from one base to another while ignoring all the comment lines (starting with '#') from an input file by command line.
I wrote a C program but when I ran it with the input file, it seems to covert the first number correctly but would just print the first result with increased line number for almost indefintely. I just have no clue on how to fix the code any suggestions would help.
My code:
#include<stdio.h>
#include<math.h>
#include<string.h>
#include<ctype.h>
void data_line(char line[])
{
int input_base;
char value[35];
int output_base;
sscanf(line, "%d %s %d", & input_base, value, & output_base);
int n = strlen(value);
int decimal = 0;
for (int i = n - 1; i >= 0; i--)
{
if (value[i] >= '0' && value[i] <= '9')
{
decimal += pow(input_base, n - i - 1) * (value[i] - '0');
}
else
{
value[i] = tolower(value[i]);
decimal += pow(input_base, n - i - 1) * (value[i] - 'a' + 10);
}
}
char output[35];
int k = 0;
while (decimal > 0)
{
int rem = decimal % output_base;
if (rem >= 10)
output[k] = ('A' + rem - 10);
else
output[k] = ('0' + rem);
k++;
decimal = decimal / output_base;
}
if (k == 0)
{
printf("0\n");
} else
{
for (int i = k - 1; i >= 0; i--)
printf("%c", output[i]);
printf("\n");
}
}
int main(int argc, char * argv[])
{
FILE * fp = fopen(argv[1], "r");
char line[100];
int line_no = 1;
while (fscanf(fp, "%[^\n]%*c", line) != EOF)
{
if (line[0] >= '0' && line[0] <= '9')
{
printf("%d: ", line_no);
data_line(line);
}
line_no++;
}
fclose(fp);
}
my input.txt samples:
# a first example
8 70 4
2 011100 16
7 0 21
11 3A 10
my output:
2: 320
3: 320
4: 320
5: 320
6: 320
7: 320
8: 320
9: 320
10: 320
11: 320
12: 320
13: 320
14: 320
15: 320
16: 320
17: 320
18: 320
19: 320
20: 320
...
...
...
expected output:
2: 320
4: 1C
5: 0
6: 43
As told in the comments always test the return codes from scanf() family functions. It is not an error for these functions to read nothing. They are scanners for formatted input, as says their names.
Also you may find easier to consume the file lines using fgets()
Example
The loop below reads the file, prints out the comment lines for testing and maybe easier to read.
while (NULL != (p = fgets(line, sizeof(line), fp)))
{
switch (line[0])
{
case '#':
printf("%s\n", line);
break;
case '\n': // blank
break;
default:
data_line(line);
break;
}
}
A complete test with a few changes
input.txt
# a first example
8 70 4
2 011100 16
7 0 21
11 3A 10
# end of input
output
# a first example
input base: 8 output base: 4
value is "70"
output value is 320
input base: 2 output base: 16
value is "011100"
output value is 1C
input base: 7 output base: 21
value is "0"
output value is 0
input base: 11 output base: 10
value is "3a"
output value is 43
# end of input
Using your code
I changed the main loop to use fgets()
added input.txt as the default file name
changed data_line() to return int so you can test for lines that are not comments but are ill-formed
grouped the output of data_line() to make it easier to read and change
added some casts to avoid the many compiler warnings
complete code
#include <ctype.h>
#include <math.h>
#include <stdio.h>
#include <string.h>
int data_line(const char* line)
{
int input_base;
char value[35];
int output_base;
int res = sscanf(
line, "%d %s %d", &input_base, value, &output_base);
if (res != 3) return -1;
size_t n = strlen(value);
int decimal = 0;
for (int i = (int)n - 1; i >= 0; i--)
{
if (value[i] >= '0' && value[i] <= '9')
{
decimal += (int)pow(
(double)input_base,
(double)(n - i - 1)) *
(value[i] - '0');
}
else
{
value[i] = tolower(value[i]);
decimal += (int)pow(
(double)input_base,
(double)(n - i - 1)) *
(value[i] - 'a' + 10);
}
}
char output[35] = {0};
int k = 0;
while (decimal > 0)
{
int rem = decimal % output_base;
if (rem >= 10)
output[k] = ('A' + rem - 10);
else
output[k] = ('0' + rem);
k++;
decimal = decimal / output_base;
}
// all output here
printf(
" input base: %d output base: %d\n value is "
"\"%s\"\n output value is ",
input_base, output_base, value);
if (k == 0)
printf("0");
else
for (int i = k - 1; i >= 0; i--)
printf("%c", output[i]);
printf("\n\n");
return 0;
}
int main(int argc, char* argv[])
{
const char* dflt = "input.txt";
FILE* fp = NULL;
if (argc < 2)
fp = fopen(dflt, "r");
else
fp = fopen(argv[1], "r");
if (fp == NULL) return -1;
char line[100];
int line_no = 0;
char* p = NULL;
while (NULL != (p = fgets(line, sizeof(line), fp)))
{
switch (line[0])
{
case '#':
printf("%s\n", line);
break;
case '\n': // blank
break;
default:
data_line(line);
break;
}
}
fclose(fp);
}
I am new to C and I am trying to scan two char variables in one line.
I used #define _CRT_SECURE_NO_WARNINGS because I have to.
This is the piece of code that annoys me: scanf_s("%c-%c", &x,1, &y,1);
When inputting 2 chars, it looks something like this ---> e-f or a-b
Also, I need to lose scanf_s and make it into scanf only.
Another thing that is kinda weird to me is that after I input two chars and do actions later, I reset values of x and y to 0 and need to input them again. However, when I type them in, I get completely different values.
Here is the whole code that I wrote so far. Task: input 2 letters and print them in order specified in input e.g. a-c --> ABC or c-a --> cba
#include<stdio.h>
#define MAX 100
int main(){
char niz[MAX];
char kraj[MAX] = {0};
int razlika;//difference
char x, y;
int i;
char trenutniznak; //current character
int stoppolje=0;//stop field
int temp = 0;
while (1)
{
scanf_s("%c-%c", &x,1, &y,1);
if (x == '.' || y == '.')
{
return 0;
}
else if (y > x)//od pocetka do kraja
{ //(from start to end )
razlika = y - x + 1;
for (i = stoppolje+1, trenutniznak=x; i <= razlika; i++, trenutniznak++)
{
niz[i] = trenutniznak;
temp = i;
}
stoppolje = temp; // polje na kojem je zadnji znak
//(the field where the last character is )
x = 0;
y = 0;
razlika = 0;
}
else
{
printf("\n");
return 0;
}
}
}
Try to understand and then implement in your code
char c;
scanf(" %c",&c); // a whitespace character in scanf() would ignore any number of whitespace characters left in the input stream
printf("%c",c);
you can go through %c conversion specifier
Scanning 2 chars in one line
Simplify. Use fgets() to read a line of user input and convert that into a string.
Avoid scanf(). It does not well read a line and is difficult to use when input is unexpected.
char buf[80];
if (fgets(buf, sizeof buf, stdin)) {
Now scan/parse the string into the two characters. Look for bad input.
char extra;
if (sscanf(buf, "%c-%c %c", &x, &y, &extra) != 2) {
fprintf(stderr, "invalid input <%s>\n", buf);
} else {
// Use x, y in some fashion
printf("Good input <%c> <%c>\n", x, y);
}
}
Sneak: If you must use scanf(), add a scanf(""); before the fgets() to use scanf(). It will not read anything.
"Task: input 2 letters and print them in order specified in input e.g. a-c --> ABC or c-a --> cba"
If scanf() is not necessary, consider using fgets(), then the resulting string array, say char buf[4] = {0}; the the array elements buf[0], buf[1] and buf[2] will contain for example 'a', '-' and 'c'. (buf[3] will contain \0 character.) These are just numeric values that happen to be within the ASCII character range, but can also be logically manipulated as numbers, so just output the sequence of numbers using a format specifier that will guarantee they appear as ASCII:
int main(void)
{
char buf[4] = {0};
int c = 0;
printf("Enter maximum of three characters: eg 'a-d'\n(or ctrl-c to exit.)\n");
while(fgets(buf, sizeof buf, stdin))
{
//test input
if((buf[0] < 'a') || (buf[0] > 'Z') ||
(buf[2] < 'a') || (buf[2] > 'Z') ||
(buf[1] != '-')
)
{
printf("Invalid input. Try again\n(or hit ctrl-c to exit.");
}
else
{
buf[strcspn(buf, "\n")] = 0;//remove newline
if(buf[0] > buf[2])
{
for(char i = buf[0];i >= buf[2];i--)
{
printf("%c", i);
}
printf("\n");
}
else if ((buf[0] < buf[2]))
{
for(char i = buf[0];i<= buf[2];i++)
{
printf("%c", toupper(i));//note UPPER CASE as specified
}
printf("\n");
}
memset(buf, 0, sizeof buf);//reset buffer for next iteration.
while((c = getchar()) != '\n' && c != EOF);//clear \n from stdin
printf("\nEnter maximum of three characters: eg 'a-d'\n(or ctrl-c to exit.)\n");
}
}
}
Well I managed somehow to do it, thank you everyone for answering my question :)
#include<stdio.h>
#define MAX 1000
int main()
{
char niz[MAX];
char kraj[MAX] = {0};
int razlika;
char x, y;
int i;
char trenutniznak;
int stoppolje=-1;
int temp = 0;
while (1)
{
scanf_s(" %c-%c", &x,1, &y,1);
if (x == '.' || y == '.')
{
for (i = 0; i <= stoppolje; i++)
{
printf("%c", niz[i]);
}
return 0;
}
else if(x == '_' || y == '_')
{
niz[stoppolje + 1] = ' ';
stoppolje += 1;
}
else if (y > x)//od pocetka do kraja
{
razlika = y - x + 1;
trenutniznak = x;
for (i = stoppolje+1, trenutniznak; i < razlika+stoppolje+1; i++, trenutniznak++)
{
niz[i] = trenutniznak;
temp = i;
}
stoppolje = temp; // polje na kojem je zadnji znak
x = 0;
y = 0;
razlika = 0;
}//ako idemo po redosljedu abecednom
else if(x>y)
{
razlika = x-y + 1;
trenutniznak = x;
for (i = stoppolje+1, trenutniznak; i < razlika + stoppolje + 1; i++, trenutniznak--)
{
niz[i] = trenutniznak;
temp = i;
}
stoppolje = temp;
x = 0;
y = 0;
razlika = 0;
}
else if (x == y)
{
razlika = 0;
trenutniznak = x;
for (i = stoppolje + 1, trenutniznak; i < razlika + stoppolje + 2; i++, trenutniznak++)
{
niz[i] = trenutniznak;
temp = i;
}
stoppolje = temp;
x = 0;
y = 0;
razlika = 0;
}
}
}
So I want to make a checksum on windows but first I need to get one string with the information of the file in binary but my code show only the information in other formats, can anyone help me get this to show this information only with 0 and 1?
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
typedef struct{
char linha[1024];
}linha;
int main() {
linha linha1;
char temp[1024];
FILE *arquivo;
if((arquivo = fopen("C:\\62-Q2.mp3","rb"))==NULL){
printf("Erro ao abrir o arquivo.");
exit(1);
}
fseek(arquivo, sizeof(linha), SEEK_SET);
while(!feof(arquivo)) {
fread(&linha1,sizeof(linha),1,arquivo);
strcpy(temp, linha1.linha);
printf("%u\n", temp);
}
fclose(arquivo);
return 0;
}
strcpy(temp, linha1.linha);
this line makes no sense at all as you do not read text file.
while(!feof(arquivo))
This is always wrong.
To dump file as bytes in bin:
void printByteAsBin(unsigned char ch)
{
unsigned char mask = 1 << (CHAR_BIT - 1);
for(; mask; mask >>= 1) printf("%c", (ch & mask) ? '1' : '0');
}
void dumpBin(FILE *fi, int linelen)
{
int ch = fgetc(fi);
int linepos = 0;
char str[linelen + 1];
while(ch != EOF)
{
printByteAsBin(ch);
str[linepos] = (isalpha(ch) || ch == ' ') ? ch : '.';
if(++linepos < linelen) printf(" ");
else {str[linepos] = 0; printf(" %s\n", str); linepos = 0;}
ch = fgetc(fi);
}
if(ch == EOF && linepos != linelen )
{
for(int x = 0; x < (linelen - linepos) * 9; x++, printf(" "));
str[linepos] = 0;
printf("%s\n", str);
}
}
You should add some error and parameter checking.
Demo:
https://godbolt.org/z/5nqeG3fE8
01001000 01100101 01101100 01101100 01101111 00100000 Hello
01010111 01101111 01110010 01101100 01100100 00001010 World.
While working through exercise 3-5 in The C Programming Language, I've come across the following unexpected behavior.
#include <stdio.h>
#include <string.h>
// inspired by: http://www.eng.uerj.br/~fariasol/disciplinas/LABPROG/C_language/Kernighan_and_Ritchie/solved-exercises/solved-exercises.html/krx305.html
void reverse(char s[]) {
int c, i, j;
for ( i = 0, j = strlen(s)-1; i < j; i++, j--) {
c = s[i];
s[i] = s[j];
s[j] = c;
}
}
void itob(int n, char s[], int b) {
static char digits[] = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ";
int i = 0,
sign;
if ( b < 2 || b > 36 ) {
fprintf(stderr, "EX3_5: Cannot support base %d\n", b);
}
if ((sign = n) < 0) {
n = -n;
}
do {
s[i++] = digits[n % b];
} while (n /= b);
if (sign < 0) {
s[i++] = '-';
}
s[i] = '\0';
reverse(s);
}
int main() {
int base = 2,
input;
char buffer[5] = "0000";
input = 127;
itob(input, buffer, base);
printf("%d in base %d is %s\n", input, base, buffer);
// 127 in base 2 is 1111111
input = 128;
itob(input, buffer, base);
printf("%d in base %d is %s\n", input, base, buffer);
// 0 in base 2 is 10000000
// Why is input now 0?!
return 0;
}
Why is the value of the input variable being changed (only when input is greater than 127)? I'm new to C but this seems very unexpected. As far as I understand, function arguments are pass-by-value.
Your buffer isn't big enough. You allocated space for 4 chars and a null terminator:
char buffer[5] = "0000";
but you're trying to stuff 8 chars and a null terminator in there with itob(input, buffer, base);. That leads to a buffer overflow and undefined behavior.
Try to use larger buffersize, with only 4 chars you can't convert number larger than 127.
I'm trying to create a code that converts a decimal into any base between 2 and 16. But I don't know how to write a new value in my string.
void base_conversion(char s[], int x, int b) {
int j, y;
j = 0;
// your code here
while(b > 1 && b < 17) {
if (x < 0) {
x = (-x);
}else if(x == 0) {
s[j] = '\0';
}
x = (x/b);
while(x > 0) {
y = (x%b);
if (y == 10) {
s[j] = 'A';
}else if(y == 11) {
s[j] = 'B';
}else if(y == 12) {
s[j] = 'C';
}else if(y == 13) {
s[j] = 'D';
}else if(y == 14) {
s[j] = 'E';
}else if(y == 15) {
s[j] = 'F';
}else{
s[j] = y;
}
}
}j = j + 1;
}
You were almost there, although several mistakes, so I have "improved" your code. The infinite loop testing the base which needed to be done once only. The while() loops weren't quite organised right - x/b being done outside the digit extraction loop. Another change I made was to use a lookup array to convert each digit to a character, which saves a lot of laborious testing. I also returned the string passed as the function value - might as well add a tad more functionality. In the case of passing a bad base value, I could have returned NULL instead of an empty string. Note also I update j in the same statements where I use it as an index, which makes the code a little more fluent.
#include <stdio.h>
char *base_conversion (char *s, int x, int b) {
char charr[] = "0123456789ABCDEF";
int i, j = 0, len, digit, neg = 0;
*s = 0; // terminate the string passed
if (b < 2 || b > 16) // check the base
return s; // return an empty string
if (x < 0) {
x = -x; // adjust for negative input
neg = 1;
}
do {
digit = x % b; // extract each l.s. digit
s[j++] = charr [digit]; // convert to character
} while (x /= b); // implicitly test for 0
if (neg) // negative input
s[j++] = '-'; // append a minus sign
s[j] = 0; // terminate the string
// reverse the string
len = j;
for (i=0; i<len/2; i++) {
digit = s[i];
s[i] = s[--j]; // pre-decrement j to next char index
s[j] = digit;
}
return s;
}
int main () {
int n;
char strig[65];
for (n=1000; n>=-1000; n-=2000) {
printf ("Binary %d: %s\n", n, base_conversion (strig, n, 2));
printf ("Ternary %d: %s\n", n, base_conversion (strig, n, 3));
printf ("Octal %d: %s\n", n, base_conversion (strig, n, 8));
printf ("Decimal %d: %s\n", n, base_conversion (strig, n, 10));
printf ("Hexadecimal %d: %s\n", n, base_conversion (strig, n, 16));
}
return 0;
}
Program output:
Binary 1000: 1111101000
Ternary 1000: 1101001
Octal 1000: 1750
Decimal 1000: 1000
Hexadecimal 1000: 3E8
Binary -1000: -1111101000
Ternary -1000: -1101001
Octal -1000: -1750
Decimal -1000: -1000
Hexadecimal -1000: -3E8