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);
}
Related
I have C program that needs to find the position of a number. It goes like this:
From standard input we enter unknown number of number that are positive. The numbers have maximum of 5 digits, we read new numbers till the user enters a value that is not a number. I need to find the positions of the max digit of a number from right to left. Use the right-most position if there are more than one instance of the max digit.
The program needs to output the position and the number of times the max digit of a number was found at that position.
For example:
input:
97654 48654 12345 12343 1263 12443 12643 12777 #
output:
0: 2
1: 3
2: 1
3: 1
4: 1
because
Position: 4 3 0 1 1 1 2 0
v v v v v v v v
97654 48654 12345 12343 1263 12443 12643 12777 #
THE PROGRAM WORKS FOR THIS SPECIFIC TEST CASE
More test cases under the code.
Here is my code:
#include <stdio.h>
int main(){
int n;
int max;
int num,digit,pos,br0=0,br1=0,br2=0,br3=0,br4=0;
while (scanf("%d",&n)) {
max =0;
num = n;
pos=0;
while (num>0) {
digit = num%10;
if(digit > max){
max=digit;
pos++;
}
num/=10;
}
printf("%d\n",pos);
switch (pos) {
case 1: br0++; break;
case 2: br1++; break;
case 3: br2++; break;
case 4: br3++; break;
case 5: br4++; break;
}
}
printf("0: %d\n1: %d\n2: %d\n3: %d\n4: %d\n",br0,br1,br2,br3,br4);
return 0;
}
This program work for some test cases, such as
97654 48654 12345 12343 1263 12443 12643 12777 #
123 456 789 987 654 321 #
But not for:
542 8965 7452 1111 12 8 6532 98745 15926 #
75386 86142 94285 15926 35724 #
The problem with your program is that within this loop
while (num>0) {
digit = num%10;
if(digit > max){
max=digit;
pos++;
}
num/=10;
}
the variable pos is incremented only when a digit that is greater than previous digits is found. For example If you have a number like this
51234
then the first largest digit is 4 and the variable pos is set to 1. After that when the next largest digit is found that is the digit 5 the variable pos is incremented and becomes equal to 2 while actually the largest digit 5 is at the position 5.
You need to introduce one more variable as for example
max =0;
num = n;
pos=1;
int i = 1;
do
{
digit = num%10;
if(digit > max){
max=digit;
pos = i;
}
} while ( ( num /=10 ) && ( i++ != 5 ) );
I would write the program the following way
#include <stdio.h>
int main(void)
{
enum { N = 5 };
const unsigned int Base = 10;
size_t total[N] = { 0 };
unsigned int n;
while ( scanf( "%u", &n ) == 1 )
{
unsigned int pos = 0;
unsigned int max_digit = 0;
unsigned int i = 0;
do
{
unsigned int current_digit = n % Base;
if ( max_digit < current_digit )
{
pos = i;
max_digit = current_digit;
}
} while ( ( n /= Base ) && ( ++i != N ) );
++total[pos];
}
for ( unsigned int i = 0; i < N; i++ )
{
printf( "%u: %zu\n", i, total[i] );
}
return 0;
}
For the input
542 8965 7452 1111 12 8 6532 98745 15926 #
the program output is
0: 3
1: 0
2: 3
3: 2
4: 1
It may be fewer steps to do the work using fgets(), and keeping the input in string format. (verifying that it contains numeric characters.)
Plus, an array of values will be easier to keep tract of value to index relationships.
Here is an alternate way of getting the information you describe:
int main(void) {
char inBuf[20] = {0};
int index = 0;
int loops = 0;
int maxPos = 0;
int maxVal = 0;
printf("Enter a number : ");
while (fgets(inBuf, sizeof inBuf, stdin) && loops < 6) {
inBuf[strcspn(inBuf, "\r\n")] = 0;//remove unwanted white space
if(strstr(inBuf, "#")) return 0;//exit if "#"
if(digits_only(inBuf))
{
index = 0;
maxVal = inBuf[index];
while(inBuf[index])
{
if(inBuf[index] >= maxVal)
{
maxVal = inBuf[index];
maxPos = index;
}
index++;
}
printf("%d:%d \n", loops, maxPos);
loops++;
inBuf[0]=0;
}
else
{
printf("\n%s contains non-numeric characters, it cannot be converted.\n\nctrl-c to exit\n...Or enter a number : \n", inBuf);
}
};
return 0;
}
scanf is the wrong tool for this. (scanf is (almost) always the wrong tool). For this particular problem, you really want to treat the input as a string. As long as you don't want to accept inputs that look like "1e3" (which is a perfectly valid representation of an integer), you could just do something like:
#include <stdio.h>
#include <assert.h>
#include <ctype.h>
#include <string.h>
int
main(void){
int max = -1;
int br[5] = {0};
int maxpos = -1;
int len = 0;
int c;
while( (c = getchar()) != EOF ){
if( c && strchr("0123456789", c) ){
if( ++len > 5 ){
fputs("invalid input\n", stderr);
return 1;
}
assert( len > 0 && len < 6 );
if( c > max + '0' ){
maxpos = len;
max = c - '0';
}
} else if( isspace(c) ){
if( max > -1 ){
br[len - maxpos] += 1;
}
maxpos = -1;
len = 0;
max = '0' - 1;
} else {
fputs("invalid input\n", stderr);
return 1;
}
}
for( int i = 0; i < 5; i++ ){
printf("%d: %d\n", i, br[i]);
}
return 0;
}
I have a large text file. In this file there are some numbers I want to add together.
What I've tried:
int sum = 0, i = 0;
file = fopen(filename, "r");
while ((i = fgetc(file)) != EOF) {
if (isdigit(i)) {
sum++;
}
}
printf("Sum of numbers is: %i", sum);
fclose(file);
But that isdigit(i) is just a counter of how many digits this file contains, not what the sum of the numbers is.
The input is: "This text 15 is very 19 nice."
The result should be: Sum of numbers is: 34
Consider the decimal placement
The missing part in the question's code is accumulating the digits (as opposed to counting the digits with sum++;) AND multiplying by ten the previous accumulated number before adding the next digit.
The answer is in:
number = number * 10 + i - '0';
The - '0' part is converting ASCII digit to a number.
Everything else in the below code is checks to make sure there are no obvious overflows and correctly supporting minus sign also adjacent to the numbers, as well as ignoring digits after the decimal point(s). I'm sure it is not perfect, but the idea here is to provide a working example of how it could be done, rather than a well tested code and using a library call to do it for you.
By popular demand (comments were deleted now) I've added a simple-but-working overflow check:
#include <stdlib.h>
#include <stdio.h>
#include <ctype.h>
#include <error.h>
#include <limits.h>
int main(int argc, char* argv[]) {
int sum = 0, state = 0, i = 0, dir = 1;
unsigned int number = 0, check;
if (argc < 2) {
fprintf(stderr, "Missing filename\n");
return EXIT_FAILURE;
}
char* filename = argv[1];
FILE* file = fopen(filename, "r");
if (!file) {
perror(filename);
return EXIT_FAILURE;
}
while (i != EOF) {
i = fgetc(file);
if (isdigit(i)) {
if (dir) {
state = 1;
check = number;
number = number * 10 + i - '0';
if (check > number || number > INT_MAX) {
fprintf(stderr, "Single number overflow error\n");
fclose(file);
return EXIT_FAILURE;
}
}
} else {
if (state && dir) {
check = number;
if (dir < 0 && sum < 0)
check -= sum;
else if (dir > 0 && sum > 0)
check += sum;
if (check > INT_MAX) {
fprintf(stderr, "Sum overflow error\n");
fclose(file);
return EXIT_FAILURE;
}
sum += number * dir;
number = 0;
}
state = 0;
dir = i == '-' ? -1 : i == '.' ? 0 : 1;
}
}
printf("Sum of numbers is: %i\n", sum);
fclose(file);
return EXIT_SUCCESS;
}
Test run:
$ cat opString.txt
This text 15 is very 19 nice.
$ ./test2 opString.txt
Sum of numbers is: 34
$
And just in case you are on 64bit linux system, and need much higher performance (you mentioned large file) the below code will map the entire file (even file larger than memory, the kernel will handle it nicely) and will not make a library call on every char. In my tests, isdigit() and strtol() slows it down significantly.
#include <stdlib.h>
#include <stdio.h>
#include <error.h>
#include <limits.h>
#include <sys/mman.h>
int addToSum(unsigned int* number, int* sum, int dir, FILE* file) {
unsigned int check;
check = *number;
if (dir < 0 && *sum < 0)
check -= *sum;
else if (dir > 0 && *sum > 0)
check += *sum;
if (check > INT_MAX) {
fprintf(stderr, "Sum overflow error\n");
fclose(file);
exit(EXIT_FAILURE);
}
*sum += *number * dir;
*number = 0;
}
int main(int argc, char* argv[]) {
int sum = 0, state = 0, i = 0, dir = 1;
unsigned int number = 0, check;
if (argc < 2) {
fprintf(stderr, "Missing filename\n");
return EXIT_FAILURE;
}
char* filename = argv[1];
FILE* file = fopen(filename, "r");
if (!file) {
perror(filename);
return EXIT_FAILURE;
}
if (fseek(file, 0L, SEEK_END) < 0) {
perror("fseek failed");
fclose(file);
return EXIT_FAILURE;
}
long fsize = ftell(file);
char* fmap = mmap(NULL, fsize, PROT_READ, MAP_SHARED, fileno(file), 0);
if (fmap == MAP_FAILED) {
perror("map failed");
fclose(file);
return EXIT_FAILURE;
}
long pos = 0;
while (pos < fsize) {
i = fmap[pos++];
if (i >= '0' && i <= '9') {
if (dir) {
state = 1;
check = number;
number = number * 10 + i - '0';
if (check > number || number > INT_MAX) {
fprintf(stderr, "Single number overflow error\n");
fclose(file);
return EXIT_FAILURE;
}
}
} else {
if (state && dir) addToSum(&number, &sum, dir, file);
state = 0;
dir = i == '-' ? -1 : i == '.' ? 0 : 1;
}
}
if (state && dir) addToSum(&number, &sum, dir, file);
printf("Sum of numbers is: %i\n", sum);
fclose(file);
return EXIT_SUCCESS;
}
Input file name: file.txt
This text 15 is very 19 nice.
Output:
Sum of numbers is: 34
Code:
#include <stdio.h>
#include <ctype.h>
int main()
{
int sum = 0, num, i = 0;
int state = 0;
FILE* f;
if ((f = fopen("file.txt", "r")) == NULL)
return -1;
while ((i = fgetc(f)) != EOF) {
switch(state) {
case 0: // processing text
if (isdigit(i)) {
num = i - '0';
state = 1;
}
break;
case 1: // processing number
if (isdigit(i)) {
num *= 10;
num += i - '0';
}
else {
sum += num;
num = 0;
state = 0;
}
break;
}
}
if (state == 1) {
sum += num;
}
printf("Sum of numbers is: %i\n", sum);
fclose(f);
return 0;
}
But that isdigit(i) is just a counter of how many digits this text contents but not whats the sum of the numbers is.
Keep in mind, the function isdigit() reads one character per call. So if it reads the character 9 for example, the value sum should grow by i - '0' (or 57 - 48, or 9). And if there were two characters in sequence, such as 92, reading one character at a time, the value sum would likewise be incremented by 9+2 -> 11, not 92. Given this is what you want, here is how to do it:
The values you have determined are digits are actually ASCII values, so in viewing this table you can see all of the digits will have values from '0' to '9' (or in ASCII, 48 to 57). So in your code you can simply change one line to sum up the value count:
int sum = 0;
file = fopen(filename, "r");
while ((i = fgetc(file)) != EOF) {
if (isdigit(i))
sum += (i - '0');//subtract 48 from every 'i' verified as digit
//Sum will therefore add up values
//between (48-48) to (57-48)
//(or between 0 to 9)
}
printf(f,"Sum of numbers is: %i", sum);
fclose(file);
However, If you want to sum up numeric values represented by a sequence of digits within a buffer, then the code is different. It would require keeping a flag as the buffer content is read.
In pseudo code:
char accumlator[10] = {0}; max possible sequential digits (change as needed)
int found = 0;//flag
int sum = 0;
while ((i = fgetc(file)) != EOF)
{
if (isdigit(i))
{
accumulator[found] = i;
found++;
}
else
{
if(found != 0)
{
sum += atoi(accumulator);
found = 0;
accumulator[0] = 0;
}
}
}
}
The function fgetc only reads a single character that is a potential digit. It does not read a whole number that consist of several digits at once. If you want to read whole numbers, you must change the logic of your program. You can do one of the following:
Use fgetc as you do now and make your program assemble the individual digits into a number, by using the ASCII Code of the digits and converting that to a number using arithmetic. The numerical value of an individual ASCII character digit can be obtained using the expression ascii_code - '0'.
Use fgets instead to read a whole line at once and then use strtol to convert any digits you find into numbers.
Use fscanf() to parse the file and extract the number in one step (not recommended unless you know exactly what you are doing).
Your program only counts the number of digits in the file. Here is a simple solution using getc() to compute the number of all integral numbers in the file without the need for a state machine:
#include <stdio.h>
int count_numbers(const char *filename) {
FILE *fp;
unsigned long long total = 0, current = 0;
int c;
if ((fp = fopen(filename, "r")) == NULL) {
printf("Cannot open input file %s\n", filename);
return -1;
}
while ((c = getc(fp)) != EOF) {
if (c >= '0' && c <= '9') {
current = current * 10 + (c - '0');
} else {
total += current;
current = 0;
}
}
total += current; /* add the last number if at the very end of the file */
printf("Sum of numbers is: %llu\n", total);
fclose(fp);
return 0;
}
Notes:
this program does not handle negative numbers, it ignores negative signs.
it does not handle floating point numbers: processing 1.1 will produce a sum of 2.
very long numbers will produce the result modulo ULLONG_MAX+1 but at least the behavior is defined.
If you want to add all the number strings up in this then you'll have to search for the digits and then convert them from a string to an integer using strtol. Here's how to do that:
#include <ctype.h>
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#define OVERFLOW_STR "Number cannot be represented in an int!\n"
int main(int argc, char **argv)
{
FILE *file;
char n[12];
char *p = n;
int i = 0, sum = 0, tmp, old_errno;
if(argc != 2)
{
fprintf(stderr, "Usage: %s file\n", argv[0]);
return EXIT_FAILURE;
}
file = fopen(argv[1], "r");
if(!file)
{
perror("Error opening file");
return EXIT_FAILURE;
}
while(i != EOF)
{
while( (i = fgetc(file)) != EOF &&
i != '-' &&
i != '+' &&
!isdigit(i) )
{
/* empty loop */ ;
}
if(i != EOF)
{
do {
if( p == n + sizeof(n) - 1 ||
((*n != '-' || *n != '+') &&
p == n + sizeof(n) - 2) )
{
fprintf(stderr, "Error: " OVERFLOW_STR);
return EXIT_FAILURE;
}
*p++ = i;
} while( (i = fgetc(file)) != EOF && isdigit(i) );
}
else
break;
*p = 0;
old_errno = errno;
errno = 0;
tmp = strtol(n, NULL, 10);
if( tmp == 0 && errno != 0 )
{
perror("Error converting string");
break;
}
errno = old_errno;
sum += tmp;
p = n;
}
printf("Sum of numbers is: %d\n", sum);
fclose(file);
return 0;
}
This is largely error-checked. There may be corner cases that I have left out.
Input (contained in a file whose name is passed as a command-line parameter):
This text 15 is very 19 nice.
Output:
Sum of numbers is: 34
I need to implement a function that can count the number of digits in a string. So for numbers but also for somehting like: aD23b. If I could make it work...it should look like:
Input: 0912302
Output:
0: 2
1: 1
2: 2
3: 1
4: 0
5: 0
6: 0
7: 0
8: 0
9: 1
At this point I can't code anything that works unfortunately...My basic idea is: Use a loop to check every character from Input, if it's a digit, store it in a second array (let's say frequency). The problems I have are that I need to somehow convert every character into a integer or somehow be able to count how
often each digits appears... I was hoping this might work but it doesn't at all:
I forgot to mention I'm a beginner in programming so I would really appreciate if you could give me tips and explanations.
void calc_occurrences(int s[], int occurrences[])
{
int i = 0;
int j;
int count = 0;
while (s[i] != '\0') {
if (isdigit(s[i])) {
for (j = 0; occurrences[j] != '\0'; j++) {
occurrences[j] = s[i];
}
}
i++;
for (j = i + 1; s[j] != '\0'; j++) {
if (isdigit(s[i]) == isdigit(s[j])) {
count++;
occurrences[j] = 0;
}
}
if(occurrences[i] != 0) {
occurrences[i] = count;
}
}
}
Make an array to count the frequency of each relevant character.
Something like this:
#include <stdio.h>
void count_freq(char* str, int freq[10])
{
int i = 0;
while(str[i]) // Loop to end of string
{
if (str[i] >= '0' && str[i] <= '9') // Check that the character is in range
{
++freq[str[i]-'0']; // notice the -'0' to get in range 0..9
}
++i;
}
}
int main(void) {
int freq[10] = {0}; // Array to count occurence
char str[] = "0034364hh324h34"; // Input string
count_freq(str, freq); // Calculate frequency
for (int i=0; i < 10; ++i) // Print result
{
printf("%d: %d\n", i, freq[i]);
}
return 0;
}
Output:
0: 2
1: 0
2: 1
3: 4
4: 4
5: 0
6: 1
7: 0
8: 0
9: 0
You can have an integer array with size 10 with 0 stored in all indices. Then, when you spot a digit, you can increment the number in the respective index.
In example, when you see a "0", you can do arr[0]++;.
Also, you may check if a character is a digit with the isdigit() function.
PS : I know, I'm answering an old post, but I was doing some challenges on HackerRank, and I managed to solve this nearly exact problem, in case it might help someone since I've used dynamic allocation on my code.
/* Problem: hackkerrank.com/challenges/frequency-of-digits-1/problem */
#include <stdio.h>
#include <string.h>
#include <math.h>
#include <stdlib.h>
int main(void)
{
char *s;
int *arr;
int i;
i = 0;
s = (char*)malloc(sizeof(char));
scanf("%s", s);
arr = (int*)malloc(10 * sizeof(int));
while(i < 10)
{
*(arr + i) = 0;
i++;
}
i = 0;
while (i < strlen(s))
{
if (*(s + i) >= '0' && *(s + i) <= '9')
{
(*(arr + (*(s + i) - '0'))) += 1;
}
i++;
}
i = 0;
while (i < 10)
{
printf("%d ", *(arr + i)); // As HackerRank problem wanted the output format.
// printf("%d: %d\n", i, *(arr + i)); As you wanted it
i++;
}
return (0);
}
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;
}
I just revised this code to accept binary numbers with fractional part. Here's the code:
#include <stdio.h>
int main()
{
double binaryval, frac, z;
long int hexadecimalval = 0, i = 1, remainder, num, p;
clrscr();
printf("Enter the binary number: ");
scanf("%lf", &binaryval);
num = binaryval;
frac = binaryval - num;
while (binaryval != 0)
{
remainder = num % 10;
hexadecimalval = hexadecimalval + remainder * i;
i = i * 2;
binaryval = binaryval / 10;
}
printf("Equivalent hexadecimal value: %lX", hexadecimalval);
printf(".");
while (binaryval != 0)
{
while(frac != 0)
{
z = frac * 2;
p = z;
frac = z - p;
}
remainder = p % 10;
hexadecimalval = hexadecimalval + remainder * i;
i = i * 2;
binaryval = binaryval / 10;
}
printf("%lX", hexadecimalval);
getch();
return 0;
}
so the problem is... it gives a totally wrong answer
SAMPLE OUTPUT:
Enter the binary number: 1111.1111
Equivalent hexadecimal value: FFFFFFFF.FFFFFFFF
And then I tried changing 'binaryval' into 'num' in this loop...
while (num != 0)
{
remainder = num % 10;
hexadecimalval = hexadecimalval + remainder * i;
i = i * 2;
binaryval = num / 10;
}
but it only lets the user input a binary number and nothing else. Also, I can't even return to my codes. It's kinda got "stuck" and I have to close my Turbo C to run it again.
Can someone please tell me what's the problem and the things that needed to be changed? Thanks in advance!
Here is a solution for you, it doesn't store very much internally as I examine the input by each character. The integral part is simple, just build the value. The fractional part is a bit more difficult, to align it correctly I have taken input 4 bits at a time.
#include <stdio.h>
#include <ctype.h>
int main(void)
{
long whole=0;
int ch, fract=0, bits=0;
// integral part is straight fowrard
while ((ch=getchar()) != EOF && ch != '.' && ch != '\n') {
switch(ch) {
case '0':
whole = whole * 2;
break;
case '1':
whole = whole * 2 + 1;
break;
default:
printf("Bad input\n");
return 1;
}
}
printf("%lX", whole);
// fractional part is more tricky to align correctly
if (ch == '.') {
printf(".");
while ((ch=getchar()) != EOF && ch != '\n') {
switch(ch) {
case '0':
fract = fract * 2;
bits++;
break;
case '1':
fract = fract * 2 + 1;
bits++;
break;
default:
printf("Bad input\n");
return 1;
}
if (bits == 4) { // process 4 bits at a time
printf("%X", fract);
fract = 0;
bits = 0;
}
}
if(bits) { // deal with 1 to 3 trailing bits
printf("%X\n", fract << (4-bits));
}
}
printf("\n");
return 0;
}
Your sample input and another:
1111.1111
F.F
1.000001
1.04
For one thing, you need to change binaryval to num in your loops.