Wrong Encryption for some Words/C - c

I'm trying to make a simple encryption program in C. My aim is to translate "jim" (it can be any word) to 10,9,13. Then plus 1,2,3(because word has 3 letter) and get 11,12,16 then again translate to text, then write on screen klp.I have 2 algorithm one for encryption and second one for return first one.It s working but for these words there s problem
"compiler you executable through" when i start first algorithm and then second algortihm(inverse) i took ."compileo vou euecrqable throrgh "i checked algorithm several times i dont know where i overlooked
PS:if u want to run make txt file which named unencrypted.txt and write word inside then first algorithm then second one
int main() {
int g,z,o,c,l,i,j,k,*D;
char alfabe[26]={'a','b','c','d','e','f','g','h','i','j','k','l','m','n','o','p','q','r','s','t','u','v','w','x','y','z'};
FILE *fp1;
FILE *mat;
char word[20];
fp1 = fopen("unencrypted.txt","r+");
do {
g = fscanf(fp1,"%s",word);
if (g != EOF) {
mat=fopen("encrypted.txt","a+");
c=strlen(word);
D=(int*)calloc(c,sizeof(int));
for(i=0;i<c;i++) {
for(j=0;j<26;j++) {
if(word[i]==alfabe[j]) {
D[i]=(((j+1)+(i+1))%26);
break;
}
}
}
}
for(z=0;z<c;z++){
o=D[z];
word[z]=alfabe[o-1];
}
for(k=0;k<c;k++) {
fprintf(mat,"%c",word[k]);
}
fprintf(mat," ");
fclose(mat);
} while (g != EOF);
fclose(fp1);
}
int main() {
int g,z,o,c,l,i,j,k,*D;
char alfabe[26]={'a','b','c','d','e','f','g','h','i','j','k','l','m','n','o','p','q','r','s','t','u','v','w','x','y','z'};
FILE *fp1;
FILE *mat;
char word[20];
fp1 = fopen("encrypted.txt","a+");
do {
g = fscanf(fp1,"%s",word);
if (g != EOF) {
mat=fopen("unencrypted1.txt","a+") ;
c=strlen(word);
D=(int*)calloc(c,sizeof(int));
for(i=0;i<c;i++) {
for(j=0;j<26;j++) {
if(word[i]==alfabe[j]) {
if(0>((j+1)-(i+1))){
D[i]=((j+1)-(i+1)+26);
} else D[i]=((j+1)-(i+1));
break;
}
}
}
}
for(z=0;z<c;z++){
o=D[z];
word[z]=alfabe[o-1];
}
for(k=0;k<c;k++) {
fprintf(mat,"%c",word[k]);
}
fprintf(mat," ");
fclose(mat);
} while (g != EOF);
fclose(fp1);
}

First, in your encryption routine, you can have undefined behaviour:
word[z]=alfabe[o-1];
accesses memory it shouldn't if o == 0, which will happen if the sum of 1-based index of the character in the word and 1-based index in the alphabet is a multiple of 26.
To avoid that, you can set word[z] = alfabe[(o+25)%26], or, simpler, leave off one of the +1s in the previous loop and use alfabe[o].
The access of alfabe[-1], if it happens, is the sort of undefined behaviour that is unlikely to cause a segmentation fault or similar drastic things, it will probably only cause an unexpected byte to be written in the encrypted word.
In the decryption, you have similar problems,
if(0>((j+1)-(i+1))){
D[i]=((j+1)-(i+1)+26);
} else D[i]=((j+1)-(i+1));
If the word is long enough, even j - i + 26 may be negative (that is, however, extremely unlikely for natural English text - also in a lot of other languages).
And again, it can happen that D[i] is set to 0, in which case
for(z=0;z<c;z++){
o=D[z];
word[z]=alfabe[o-1];
}
will again access alfabe[-1] and cause undefined behaviour.
Note also that if your input contains anything except lower case letters and whitespace (which is discarded by the fscanf), the corresponding entry of the D array contains whatever bytes occurred to be at that memory location, again causing undefined behaviour.
If your input contains only lower case letters (and whitespace), you can find the index in the alphabet much easier than with the loop by setting
j = word[i] - 'a';
To avoid the alfabe[-1] issues, I recommend using 0-based indices.

Related

Count Different Character Types In String

I wrote a program that counts and prints the number of occurrences of elements in a string but it throws a garbage value when i use fgets() but for gets() it's not so.
Here is my code:
#include<stdio.h>
#include<string.h>
#include<ctype.h>
#include<stdlib.h>
int main() {
char c[1005];
fgets(c, 1005, stdin);
int cnt[26] = {0};
for (int i = 0; i < strlen(c); i++) {
cnt[c[i] - 'a']++;
}
for (int i = 0; i < strlen(c); i++) {
if(cnt[c[i]-'a'] != 0) {
printf("%c %d\n", c[i], cnt[c[i] - 'a']);
cnt[c[i] - 'a'] = 0;
}
}
return 0;
}
This is what I get when I use fgets():
baaaabca
b 2
a 5
c 1
32767
--------------------------------
Process exited after 8.61 seconds with return value 0
Press any key to continue . . . _
I fixed it by using gets and got the correct result but i still don't understand why fgets() gives wrong result
Hurray! So, the most important reason your code is failing is that your code does not observe the following inviolable advice:
Always sanitize your inputs
What this means is that if you let the user input anything then he/she/it can break your code. This is a major, common source of problems in all areas of computer science. It is so well known that a NASA engineer has given us the tale of Little Bobby Tables:
Exploits of a Mom #xkcd.com
It is always worth reading the explanation even if you get it already #explainxkcd.com
medium.com wrote an article about “How Little Bobby Tables Ruined the Internet”
Heck, Bobby’s even got his own website — bobby-tables.com
Okay, so, all that stuff is about SQL injection, but the point is, validate your input before blithely using it. There are many, many examples of C programs that fail because they do not carefully manage input. One of the most recent and widely known is the Heartbleed Bug.
For more fun side reading, here is a superlatively-titled list of “The 10 Worst Programming Mistakes In History” #makeuseof.com — a good number of which were caused by failure to process bad input!
Academia, methinks, often fails students by not having an entire course on just input processing. Instead we tend to pretend that the issue will be later understood and handled — code in academia, science, online competition forums, etc, often assumes valid input!
Where your code went wrong
Using gets() is dangerous because it does not stop reading and storing input as long as the user is supplying it. It has created so many software vulnerabilities that the C Standard has (at long last) officially removed it from C. SO actually has an excellent post on it: Why is the gets function so dangerous that it should not be used?
But it does remove the Enter key from the end of the user’s input!
fgets(), in contrast, stops reading input at some point! However, it also lets you know whether you actually got an entire line of of text by not removing that Enter key.
Hence, assuming the user types: b a n a n a Enter
gets() returns the string "banana"
fgets() returns the string "banana\n"
That newline character '\n' (what you get when the user presses the Enter key) messes up your code because your code only accepts (or works correctly given) minuscule alphabet letters!
The Fix
The fix is to reject anything that your algorithm does not like. The easiest way to recognize “good” input is to have a list of it:
// Here is a complete list of VALID INPUTS that we can histogram
//
const char letters[] = "abcdefghijklmnopqrstuvwxyz";
Now we want to create a mapping from each letter in letters[] to an array of integers (its name doesn’t matter, but we’re calling it count[]). Let’s wrap that up in a little function:
// Here is our mapping of letters[] ←→ integers[]
// • supply a valid input → get an integer unique to that specific input
// • supply an invalid input → get an integer shared with ALL invalid input
//
int * histogram(char c) {
static int fooey; // number of invalid inputs
static int count[sizeof(letters)] = {0}; // numbers of each valid input 'a'..'z'
const char * p = strchr(letters, c); // find the valid input, else NULL
if (p) {
int index = p - letters; // 'a'=0, 'b'=1, ... (same order as in letters[])
return &count[index]; // VALID INPUT → the corresponding integer in count[]
}
else return &fooey; // INVALID INPUT → returns a dummy integer
}
For the more astute among you, this is rather verbose: we can totally get rid of those fooey and index variables.
“Okay, okay, that’s some pretty fancy stuff there, mister. I’m a bloomin’ beginner. What about me, huh?”
Easy. Just check that your character is in range:
int * histogram(char c) {
static int fooey = 0;
static int count[26] = {0};
if (('a' <= c) && (c <= 'z')) return &count[c - 'a'];
return &fooey;
}
“But EBCDIC...!”
Fine. The following will work with both EBCDIC and ASCII:
int * histogram(char c) {
static int fooey = 0;
static int count[26] = {0};
if (('a' <= c) && (c <= 'i')) return &count[ 0 + c - 'a'];
if (('j' <= c) && (c <= 'r')) return &count[ 9 + c - 'j'];
if (('s' <= c) && (c <= 'z')) return &count[18 + c - 's'];
return &fooey;
}
You will honestly never have to worry about any other character encoding for the Latin minuscules 'a'..'z'.Prove me wrong.
Back to main()
Before we forget, stick the required magic at the top of your program:
#include <stdio.h>
#include <string.h>
Now we can put our fancy-pants histogram mapping to use, without the possibility of undefined behavior due to bad input.
int main() {
// Ask for and get user input
char s[1005];
printf("s? ");
fgets(s, 1005, stdin);
// Histogram the input
for (int i = 0; i < strlen(s); i++) {
*histogram(s[i]) += 1;
}
// Print out the histogram, not printing zeros
for (int i = 0; i < strlen(letters); i++) {
if (*histogram(letters[i])) {
printf("%c %d\n", letters[i], *histogram(letters[i]));
}
}
return 0;
}
We make sure to read and store no more than 1004 characters (plus the terminating nul), and we prevent unwanted input from indexing outside of our histogram’s count[] array! Win-win!
s? a - ba na na !
a 4
b 1
n 2
But wait, there’s more!
We can totally reuse our histogram. Check out this little function:
// Reset the histogram to all zeros
//
void clear_histogram(void) {
for (const char * p = letters; *p; p++)
*histogram(*p) = 0;
}
All this stuff is not obvious. User input is hard. But you will find that it doesn’t have to be impossibly difficult genius-level stuff. It should be entertaining!
Other ways you could handle input is to transform things into acceptable values. For example you can use tolower() to convert any majuscule letters to your histogram’s input set.
s? ba na NA!
a 3
b 1
n 2
But I digress again...
Hang in there!

Printing all duplicate words from a file in C

What am I supposed to do in order to print only the duplicated words in a file. I'm a beginner in C.
Here's my code:
#include <stdio.h>
#define max 100
main(){
FILE *fpointer;
char ch[max],str[max][max];
int i = 0,j = 0,count = 0;
int a,b;
fpointer = fopen("1.txt","r");
if(fpointer == NULL){
printf("Error!");
return 0;
}
while(fscanf(fpointer,"%s",ch)!=EOF){
strcat(str[i++],ch);
strcat(str[i++]," ");
count++;
}
for(a=0;a<count*2;a=a+2){
for(b=0;b<count*2;b=b+2){
if(strcmp(str[a],str[b])==0){
printf("%s\n",str[b]);
}
}
}
fclose(fpointer);
return 0;
}
An example of input is "shine little star shine bright in the dark star"
and the output of this program is "shine shine little star star shine shine bright in the dark star star". Please help me to print
only duplicate words. Tnx
In your inner loop, instead of starting at the beginning of the list, start one element after the current one. That way you're not comparing an element against itself or comparing any two elements twice.
for(a=0;a<count*2;a=a+2){
for(b=a+2;b<count*2;b=b+2){ // b=a+2 instead of b=0
if(strcmp(str[a],str[b])==0){
printf("%s\n",str[b]);
}
}
}
Output:
shine
star
Since you wrote duplicate words, i am guessing the words in the file will be repeated twice only.
So you second nested for loop is starting from the beginning every time, and thats where the problem is. You have to make sure that the word already checked is not iterated over again. For that you can change your second for loop a little
for(a=0;a<count*2;a=a+2){
for(b=a+2;b<count*2;b=b+2){ //notice b=a+2
if(strcmp(str[a],str[b])==0){
printf("%s\n",str[b]);
}
}
}
Note
For cases when the words are repeating more then twice, my suggestion would be to use some hashing technique to store if a word has already occurred or not. And then compare and print accordingly.
You have a couple of problems here.
Go through and write some comments. For example, put these where they belong:
// Open the file
// Read in the strings
// Compare the strings
If you do this you will notice you have some problems. For instance, what is this line for?
strcat(str[i++]," ");
It's not reading in a string. So what is it doing?
In your final loop, you want to compare each string with all the others. So obviously, you start with the first string, and you compare it to all the others:
for (a=1; a<count; a++)
if(strcmp(str[0],str[a])==0)
{
printf("%s\n",str[0]);
break;
}
Now we can compare the first string with each following string and print it out if it is duplicated. The "break" is there so we only print it once.
But this only compares the first string with the others. How do we compare all of them? Another loop, of course.
// Compare each string against all the ones after it
for (target=0; target<count; target++)
for (a=target+1; a<count; a++)
// If the strings match AND they are not both null
// (why print out copies of null strings? - nobody wants that)
if(strcmp(str[target],str[a])==0) && strlen(str[a])
{
// Print it
printf("%s\n",str[target]);
// Why is this here?
strcpy(str[a],"");
for (copies=a+1; copies<count; copies++)
if(strcmp(str[copies],str[target])==0)
strcpy(str[copies],"");
// We got a match, stop checking for this string
break;
}
See how we've got one big loop that steps through each string, and a smaller loop that steps through all the remaining strings (no point in stepping through the ones we already looked at).
Now a question for you: why is "strcpy(str[a],"") and the next for loop in there? What purpose do they serve?
This is one way to do it. Please enable warnings - you left out a library header. I've also checked array limits. You need to find a better way to limit the string input too, than just guess a large array size.
#include <stdio.h>
#include <stdlib.h>
#include <string.h> // included header
#define MAX 100
int main(void){ // correct function
FILE *fpointer;
int reported [MAX];
char ch[MAX], str[MAX][MAX];
int index = 0, a;
fpointer = fopen("1.txt","r");
if(fpointer == NULL){
printf("Error!");
return 0;
}
while(fscanf(fpointer,"%s",ch) == 1){ // testing for 1 item read
for(a=0; a<index; a++){
if(strcmp(ch, str[a])==0){
if (reported[a] == 0) // don't report twice
printf("%s ",ch);
reported[a]++;
break;
}
}
if(a >= index) { // check if loop completed
if (index >= MAX) { // check if array is full
printf("\nToo many words read\n");
exit(1);
}
strcpy(str[index], ch); // word was not found
reported[index] = 0;
index++; // keep tabs on array length
}
}
printf("\n",ch);
fclose(fpointer);
return 0;
}
Program output:
shine star
One issue is that you are storing every word, regardless of whether it has already been seen:
while(fscanf(fpointer,"%s",ch)!=EOF){
strcat(str[i++],ch);
strcat(str[i++]," "); // ??? what is this for
count++;
}
Ideally, you'd only like to store each word once, and maintain a count of how often that word appears in the file:
while ( fscanf( fpointer, "%s", ch ) != EOF ) // whitespace is your friend, use it
{
if ( ch_is_not_already_in_str )
{
strcpy( str[count++], ch );
}
else
{
update_count_for_that_string;
}
}
Then at the end, you just loop through ch and print each string that has a count greater than 1.
for ( i = 0; i < count; i++ )
{
if ( str[i]_occurs_more_than_once )
printf( "%s ", str[i] );
}
The bits you need to figure out are ch_is_not_already_in_str, update_count_for_that_string, and str[i]_occurs_more_than_once. How would you go about searching your str array for an occurence of the string in ch? How would you maintain a counter for each string?

Trying to find frequency of certain characters in a string but the results are very far off

I am trying to write a program which calculates and prints the GC content of a string of DNA(which is input through a txt file). That is, the percentage of G's and C's in a string of DNA. Here is my function for the GC percentage:
void updateGCCount(char s[], int *gc, int *at) {
char c[MAXLENGTH];
int i,GCcount,ATcount;
float len,GCpercentage;
GCcount=0;
ATcount=0;
for(i=0;c[i]!='\0';++i)
{
if(c[i]=='G' || c[i]=='C')
{
++GCcount;
*gc=GCcount;
}
if(c[i]=='A' || c[i]=='T')
{
++ATcount;
*at=ATcount;
}
}
strcpy(c,s);
len=strlen(c);
GCpercentage=*gc/len;
printf("GC-content: %.2f\n",GCpercentage);
}
This is my function definition, and the part which is supposed to correctly print the GC percentage is what I am not sure about. Below is my main program which utilizes the input text file.
#include "genomics.h"
int main(){
char s[MAXLENGTH];
int gc, at;
scanf("%s",s);
printf("Sequence : %s\n",s);
updateGCCount(s, &gc, &at);
return 0;
}
Any help or advice on why I am not getting a correct value for the GCpercentage would be great. Thank you in advance
You're doing your tests on char array "c":
char c[MAXLENGTH];
...
for(i=0;c[i]!='\0';++i)
{
if(c[i]=='G' || c[i]=='C')
{
++GCcount;
*gc=GCcount;
}
if(c[i]=='A' || c[i]=='T')
{
++ATcount;
*at=ATcount;
}
}
If should be on s, the array that you passed in. The c array is probably superflous, you should be able to get the length from s as well
c is not initialize, so *gc and *at are not updated at all and they contain garbage..
here you should use s instead of c
for(i=0;c[i]!='\0';++i)
{
if(c[i]=='G' || c[i]=='C')
{
++GCcount;
*gc=GCcount;
}
if(c[i]=='A' || c[i]=='T')
{
++ATcount;
*at=ATcount;
}
}
That's a strongly un-idiomatic program. Consider the following.
#include <stdio.h>
#include <stdlib.h> /* for exit(3) */
float count_gc(const char* s)
{
You have no need to pass information back via variables passed in by reference. Functions return values -- typically 'the answer'.
You're simply scanning the content of the argument string s, so there's no need to copy it anywhere.
As others have pointed out, you were scanning the contents of the array c[] before you copied anything in to it -- you were counting 'G' and 'C' in a (probably large) random block of memory. Keeping things simple avoids mistakes like that.
int nvalid = 0;
int gccount = 0;
float result;
for (; *s != '\0'; s++) {
Although the for loop you wrote isn't wrong, it's somewhat un-idiomatic. Here, we examine the character pointed to by the pointer s, and then increment the pointer, until we find ourselves pointing at the \0 that terminates the string. Yes, this means we 'lose' the initial value of the argument, but we don't need it after the loop, so that doesn't matter.
switch (*s) {
A switch is a more natural construction here. You're looking for a small set of possible values that *s (that is, the character the pointer is currently pointing at) may have.
case 'G':
case 'C':
nvalid++;
gccount++;
break;
case 'A':
case 'T':
nvalid++;
break;
default:
/* unexpected character -- ignore it */
break;
Every switch statement should have a default clause -- one should always think of what's supposed to happen if none of the case clauses match. In this case, we just ignore this character.
}
}
if (nvalid == 0) {
fprintf(stderr, "No valid letters found!\n");
result = 0.0;
} else {
/* Multiply by 1.0 to convert integer gccount to a float */
result = 1.0*gccount / nvalid;
}
return result;
We return the result to the caller rather than printing it out inside the function. Functions shouldn't 'chatter', but leave all of the I/O in one place, typically leaving the main function (or something higher up) to look after that.
}
int main(int argc, char** argv)
{
if (argc != 2) {
/* Give the user a hint on how to call the program */
fprintf(stderr, "Usage: gcat <string>\n");
exit(1);
}
printf("Sequence GC-content = %g\n", count_gc(argv[1]));
}
I run that with:
% cc -o gcat gcat.c
% ./gcat "GCAT ATx foo"
Sequence GC-content = 0.333333
%
With C, it's very easy to tie yourself in knots, very quickly. Aim for simplicity always.

Caesar Cipher Program - Absurd Number in Array Output

I'm actually writing about the same program as before, but I feel like I've made significant progress since the last time. I have a new question however; I have a function designed to store the frequencies of letters contained within the message inside an array so I can do some comparison checks later. When I ran a test segment through the function by outputting all of my array entries to see what their values are, it seems to be storing some absurd numbers. Here's the function of issue:
void calcFreq ( float found[] )
{
char infname[15], alpha[27];
char ch;
float count = 0;
FILE *fin;
int i = 0;
while (i < 26) {
alpha[i] = 'A' + i++;
}
printf("Please input the name of the file you wish to scan:\n");
scanf("%s", infname);
fin = fopen ( infname, "r");
while ( !feof(fin) ) {
fscanf(fin, "%c", &ch);
if ( isalpha(ch) ) {
count += 1;
i = 0;
if ( islower(ch) ) { ch = toupper(ch); }
while ( i < 26 ) {
if ( ch == alpha[i] ) {
found[i]++;
i = 30;
}
i++;
}
}
}
fclose(fin);
i = 0;
while ( i < 26 ) {
found[i] = found[i] / count;
printf("%f\n", found[i]);
i++;
}
}
At like... found[5], I get this hugely absurd number stored in there. Is there anything you can see that I'm just overlooking? Also, some array values are 0 and I'm pretty certain that every character of the alphabet is being used at least once in the text files I'm using.
I feel like a moron - this program should be easy, but I keep overlooking simple mistakes that cost me a lot of time >.> Thank you so much for your help.
EDIT So... I set the entries to 0 of the frequency array and it seems to turn out okay - in a Linux environment. When I try to use an IDE from a Windows environment, the program does nothing and Windows crashes. What the heck?
Here are a few pointers besides the most important one of initializing found[], which was mentioned in other comments.
the alpha[] array complicates things, and you don't need it. See below for a modified file-read-loop that doesn't need the alpha[] array to count the letters in the file.
And strictly speaking, the expression you're using to initialize the alpha[] array:
alpha[i] = 'A' + i++;
has undefined behavior because you modify i as well as use it as an index in two different parts of the expression. The good news is that since you don't need alpha[] you can get rid of its initialization entirely.
The way you're checking for EOF is incorrect - it'll result in you acting on the last character in the file twice (since the fscanf() call that results in an EOF will not change the value of ch). feof() won't return true until after the read that occurs at the end of the file. Change your ch variable to an int type, and modify the loop that reads the file to something like:
// assumes that `ch` is declared as `int`
while ( (ch = fgetc(fin)) != EOF ) {
if ( isalpha(ch) ) {
count += 1;
ch = toupper(ch);
// the following line is technically non-portable,
// but works for ASCII targets.
// I assume this will work for you because the way you
// initialized the `alpha[]` array assumed that `A`..`Z`
// were consecutive.
int index = ch - 'A';
found[index] += 1;
}
}
alpha[i] = 'A' + i++;
This is undefined behavior in C. Anything can happen when you do this, including crashes. Read this link.
Generally I would advise you to replace your while loops with for loops, when the maximum number of iterations is already known. This makes the code easier to read and possibly faster as well.
Is there a reason you are using float for counter variables? That doesn't make sense.
'i = 30;' What is this supposed to mean? If your intention was to end the loop, use a break statement instead of some mysterious magic number. If your intention was something else, then your code isn't doing what you think it does.
You should include some error handling if the file was not found. fin = fopen(..) and then if(fin == NULL) handle errors. I would say this is the most likely cause of the crash.
Check the definition of found[] in the caller function. You're probably running out of bounds.

Simple unit converter in C

I purchased "A Book on C" for my procedural programming class and I was going through some of the exercises. Chapter 2 Exercise 9 is about designing a unit converter that can work with ounces, pounds, grams, and kilograms.
The code I've written works, but I really think it could be done much cleaner. Using nested if statements seems like a messy way to go about this.
Also, one problem I noticed was that if a char or string is given to scanf() on line 27, it will persist and then be passed to the scanf() on line 95. For example, if you enter "y" as the value to convert, the program will goto beginning without allowing the user to answer "Would you like to perform additional conversions?" How can I go about fixing this so that if a NaN is input it is discarded?
My code can be located at:
http://pastebin.com/4tST0i7T
One way to clean up the if structure would be to convert the value from the "fromUnit" to a common value and then convert it to the "toUnit". It simplifies the structure by leaving only two if structures around. (It also scales better.) So, it would be something more like:
if (!strcmp(fromUnit, "pound")) {
tempval = input / 16;
} else if (!strcmp(fromUnit, "gram") == 0) {
tempval = input * OUNCESTOGRAMS;
}
if (!strcmp(toUnit, "pound")) {
output = tempval * 16;
} else if (!strcmp(toUnit, "gram")) {
output = tempval / OUNCESTOGRAMS;
}
Granted, that math isn't correct, it's just there for the example. You would just have to (1) pick the temporary unit that you wanted to use (2) convert from the input unit to that unit and (3) convert from the temporary unit to the output unit.
And as someone else mentioned, gets() is definitely the way to go.
I would do it something like this:
#include <stdio.h>
typedef struct _unit {
char * name;
float grams;
} unit;
unit units[] = {
{"gram", 1.0},
{"kilogram", 1000.0},
{"pound", 500.0},
{"ounce", 28.3495231}
};
unit * search_unit(char * name)
{
int i;
for (i = 0; i < (sizeof(units) / sizeof(unit)); i++)
{
printf("%d %s\n", i, units[i].name);
if (0 == strcmp(units[i].name, name))
{
return & units[i];
}
}
return NULL;
}
int main() {
char line[10];
char unitname[10];
int number;
unit * found_unit;
while (1)
{
fgets(line, sizeof(line), stdin);
if (1 == sscanf(line, "%d", &number))
{
break;
}
printf("not a number\n");
}
while (1)
{
fgets(line, sizeof(line), stdin);
sscanf(line, "%s\n", unitname);
found_unit = search_unit(unitname);
if (found_unit)
{
printf("%d %s is %f grams\n", number, unitname, found_unit->grams * number);
break;
}
printf("unknown unit\n");
}
}
Store your data in some data structure, instead of in the code.
First read a line of text, then check whether it is a number.
When reading from stdin, take the size of the buffer into account.
Use loops instead of goto's.
Use some common unit, grams for example, to calculate anything to anything.
The most reliable way is to read input string using fgets() function, check if it contains digit using isdigit() (all characters in string) and then convert it to numeric value using atoi().
BTW, the last two operations can be replaced by strtol().

Resources