Common character to indicate error has occured - c

Typically in a function that returns an integer, -1 is used to indicate to the user/programmer that a error has occurred in the program. e.g. A program may return -1 instead of the index of where a word starts in a string if the word cannot be found in the string.
So my question is, what should you return from a function that returns a character instead?
In other words, is there a common character that most programmers use like -1 to detect when an error has occurred?
It may be subjective to the scenario of course so lets say you created a program that converts a digit into its corresponding character:
char digitToCh (int digit)
{
if ( digit < 0 ) // If digit is negative, return ?
return '?';
int n = intLen (digit);
if ( n > 1 )
digit /= power (10, n - 1);
return (char) (((int) '0') + digit);
}
If the digit is negative, what error character code would seem appropriate. This example is quite arbitrary but I'm just trying to make it simpler to express.

No, there is no such reserved character. In fact, it is the absence of such common character that caused character-oriented I/O functions to change their return type to int.
The trick with returning an int is important when you must retain the full range of character return values. In your case, however, only the ten digit characters are valid, so returning character zero '\0' is a valid option:
char digitToCh (int digit) {
if (digit < 0) return '\0';
...
}
You can check the error like this:
char nextDigit = digitToCh(someInt);
if (!nextDigit) {
// The next digit is invalid
}

Make the function return an int instead of a char and also use -1 to indicate an error.
This way it is done by fgetc() for example.
.

Related

Checking just integers and no strings using fgets only

I'm trying to get an integer number from command line without scanf() but using justfgets(), how can I filter the fgets() contentsreporting an error if I insert a character or a string? The problem is that when I insert something different like a character or a string the atoi()function (essential to do some operations in my algorithm) converts me that string to 0, whilst I'd prefer to exit if the value inserted is different from an integer.
Here's a code part:
.....
char pos[30];
printf("\n Insert a number: ");
fgets (pos, sizeof(pos), stdin);
if (atoi(pos) < 0) //missing check for string character
exit(1);
else{
printf ("%d\n", atoi(pos)); //a string or character converted through atoi() gives 0
}
int number = atoi(pos);
......
As commenters have said, use strtol() not atoi().
The problem with strtol() is that it will only give an ERANGE error (as per the specification) when the converted number will not fit in a long-type. So if you ask it to convert " 1" it gives 1. If you ask it to convert "apple", it returns 0 and sets endptr to indicate an error.
Obviously you need to decide if " 12" is going to be acceptable input or not — strtol() will happily skip the leading white space.
EDIT: Function updated to better handle errors via the endptr.
// Convert the given <text> string to a decimal long, in <value>
// Allow a string of digits, or white space then digits
// returns 1 for OK, or 0 otherwise
int parseLong( const char *text, long *value )
{
int rc = 0; // fail
char *endptr; // used to determine failure
if ( text && value )
{
errno = 0; // Clear any errors
*value = strtol( text, &endptr, 10 ); // Do the conversion
// Check that conversion was performed, and
// that the value fits in a long
if ( endptr != text && errno != ERANGE )
{
rc = 1; // success
}
}
return rc;
}
First, you have to keep in mind that characters are not essentially alpha characters; be precise.
I think what you're looking for is an "is integer" function.
In the standard C library ctype.h there are functions called isalpha and isdigit.
https://www.programiz.com/c-programming/library-function/ctype.h/isalpha
So you could make a function that verifies if a char * contains only numeric characters.
int str_is_only_numeric(const char *str) {
int i = 0;
while (str[i] != '\0') {
if (isdigit(str[i++]) == 0) {
return -1;
}
}
return 0;
}
Here's a working example of the function: https://onlinegdb.com/SJBdLdy78
I solved on my own using strcspn()before checking through isdigit()the integer type, without strcspn() it'd have returned always -1

CS50 Pset 2- ceasar cipher

It isn't showing what I want it to show that is the ciphered version of the input text but instead symbols, as I guess, looks kinda like a '?' comes out as the output in the terminal. could anyone help me in finding what I missed or did wrong?
#include <cs50.h>
#include <stdio.h>
#include <string.h>
int main(int argc, string argv[])
{
if (argc == 2)
{
string ptext = get_string("plaintext: ");
int key = (int) argv[1];
printf("ciphertext: ");
for (int i = 0, n = strlen(ptext); i < n; i++)
{
printf("%c", (( ptext[i] + key ) % 26);
}
printf("\n");
}
else
{
printf("Invalid input. \n");
}
}
I expect the output of 'hello' to be 'ifmmp' but instead, it doesn't.
This code is wrong:
int key = (int) argv[1];
argv is an array of string, which in CS50 is nothing more than an obfuscated char * pointer.
Per 5.1.2.2.1 Program startup of the C standard:
The function called at program startup is named main. The implementation declares no prototype for this function. It shall be defined with a return type of int and with no parameters:
int main(void) { /* ... */ }
or with two parameters (referred to here as argc and argv, though any names may be used, as they are local to the function in which they are declared):
int main(int argc, char *argv[]) { /* ... */ }
or equivalent; ...
So argv[1] is a char * pointer value, which you then assign to an int value. That's taking the address of some memory (say the value in argv[1] is 0xFF0403220020480C) and trying to stuff it into the likely 4 bytes of the int variable key (which in this case would be assigned the truncated value 0x0020480C.)
That's not what you appear to be trying to do.
(IMO, your problem here is a perfect example of why CS50's obfuscation of char * with the string type is a tremendously bad idea. You simply can't understand C without understanding pointers and NUL-terminated char strings accessed via a char * pointer, and the obfuscation that string does makes that harder.)
If you want to convert a string to a numeric value, you likely want something like strtol() (never use atoi() as it has no error checking and its use can invoke undefined behavior):
char firstCharNotConverted;
// set errno to zero as strtol()
errno = 0;
long key = strtol( argv[ 1 ], &firstCharNotConverted, 0 );
// if errno is now non-zero, the call to strtol() failed (per Linux man page)
// need to examine key and the contents of firstCharNotConverted
// to figure out why
if ( errno != 0 )
{
...
}
Proper headers omitted as an exercise for anyone trying to use this code ;-)
Note that I used long for key, as you can't do complete and proper error checking on strtol() if you cast the return value to int.
Error checking strtol() can be somewhat complex as the value returned (and assigned to key in the above code) can be any value and there are no values possible that aren't legitimate long values that strtol() can return to indicate an error, so for proper error checking you need to check the values of both errno and firstCharNotConverted to properly determine if an error did occur. The Linux man page states:
Since strtol() can legitimately return 0, LONG_MAX, or LONG_MIN
(LLONG_MAX or LLONG_MIN for strtoll()) on both success and failure,
the calling program should set errno to 0 before the call, and then
determine if an error occurred by checking whether errno has a
nonzero value after the call.
After that call to strtol(), you need to check if key is LONG_MIN, or LONG_MAX with errno equal to ERANGE for underflow or overflow, or if key is 0 you need to check the contents of firstCharNotConverted to determine why the conversion may have failed. Note that if key is zero and firstCharNotConverted isn't equal to argv[ 1 ], then input string was properly converted from zero.
Your Ceaser cipher implementation is also wrong:
for (int i = 0, n = strlen(ptext); i < n; i++)
{
printf("%c", (( ptext[i] + key ) % 26);
}
printf("\n");
will just print out characters with values from 0 through 25 - which aren't letters in the ASCII character set.
There a numerous Ceaser ciphers questions already posted here, so I'm not going to write the code. See Caesar's Cipher Code for one example question.
The problem is here printf("%c", (( ptext[i] + key ) % 26);. Specifically with % 26. It certainly looks exactly like the problem set:
More formally, if p is some plaintext (i.e., an unencrypted message),
pi is the ith character in p, and k is a secret key (i.e., a
non-negative integer), then each letter, ci, in the ciphertext, c, is
computed as
ci = (pi + k) % 26
wherein % 26 here means “remainder when dividing by 26.”
But, the pset goes on to say:
think of A (or a) as 0, B (or b) as 1, …, H (or h) as 7, I (or i) as
8, …, and Z (or z) as 25.
The problem is that the character ptext[i] is the ascii value of the letter, not the "alphabet index".
Perhaps review the spoiler in the pseudocode section of the Lab, particularly #5:
Iterate over each character of the plaintext:
If it is an uppercase letter, rotate it, preserving case, then print out the rotated character
If it is a lowercase letter, rotate it, preserving case, then print out the rotated character
If it is neither, print out the character as is
You may find this walkthrough (from an earlier iteration of the course) helpful.

atoi ignores a letter in the string to convert

I'm using atoi to convert a string integer value into integer.
But first I wanted to test different cases of the function so I have used the following code
#include <stdio.h>
int main(void)
{
char *a ="01e";
char *b = "0e1";
char *c= "e01";
int e=0,f=0,g=0;
e=atoi(a);
f=atoi(b);
g=atoi(c);
printf("e= %d f= %d g=%d ",e,f,g);
return 0;
}
this code returns e= 1 f= 0 g=0
I don't get why it returns 1 for "01e"
that's because atoi is an unsafe and obsolete function to parse integers.
It parses & stops when a non-digit is encountered, even if the text is globally not a number.
If the first encountered char is not a space or a digit (or a plus/minus sign), it just returns 0
Good luck figuring out if user input is valid with those (at least scanf-type functions are able to return 0 or 1 whether the string cannot be parsed at all as an integer, even if they have the same behaviour with strings starting with integers) ...
It's safer to use functions such as strtol which checks that the whole string is a number, and are even able to tell you from which character it is invalid when parsing with the proper options set.
Example of usage:
const char *string_as_number = "01e";
char *temp;
long value = strtol(string_as_number,&temp,10); // using base 10
if (temp != string_as_number && *temp == '\0')
{
// okay, string is not empty (or not only spaces) & properly parsed till the end as an integer number: we can trust "value"
}
else
{
printf("Cannot parse string: junk chars found at %s\n",temp);
}
You are missing an opportunity: Write your own atoi. Call it Input2Integer or something other than atoi.
int Input2Integer( Str )
Note, you have a pointer to a string and you will need to establish when to start, how to calculate the result and when to end.
First: Set return value to zero.
Second: Loop over string while it is not null '\0'.
Third: return when the input character is not a valid digit.
Fourth: modify the return value based on the valid input character.
Then come back and explain why atoi works the way it does. You will learn. We will smile.

Unexpected Results Using isdigit(x)

I am using the following code. I expect the output to be "Yes", but I instead get "No." I must be missing something very simple and fundamental.
#include <stdio.h>
#include <ctype.h>
int main(void)
{
int n = 3;
if (isdigit(n))
{
printf("Yes\n");
}
else
{
printf("No\n");
}
return 0;
}
isdigit checks whether the character passed to it is a numeric character. Therefore, its argument should be char type or int type which is the code of a character.
Here, you are passing 3 to isdigit. In ASCII, 3 is the code of the character ETX (end of text) which is a non-numeric character. Therefore, isdigit(3) returns false.
isdigit() expects a character code, while you expect it to accept a plain number.
Those expectations do not mesh well.
Character literals:
'0' ordinal 0x30 -- 48
'1' ordinal 0x31 -- 49
'2' ordinal 0x32 -- 50
... You get the drift
'3' is not a digit, here, in the way isdigit() considers them. Change int n = 3 to int n = '3'. If your system uses ASCII, for instance, 3 is the end of text marker, whereas 51, equivalent to '3', is the actual character three.
isdigit expects a character. If the character passed is digit then it returns non zero. 3 is an ASCII value of non-printable character, i.e it doesn't represent a digit and that's why your else body gets executed.
man isdigit:
These functions check whether c, which must have the value of an
unsigned char or EOF, falls into a certain character class according to
the current locale.
3 is an integer, not a character. In fact, 3 is also the value of a character that is not a digit.
The string "\03\t\nABCabc123" is composed of 12 characters. The first 9 characters all return false when applied to the isdigit() function. The last 3 all return true;
#include <ctype.h>
#include <stdio.h>
int main(void) {
char data[] = "\03\t\nABCabc123";
// data[0] = 3;
char *ptr = data;
while (*ptr) {
printf("%4d --> %s\n", *ptr, isdigit((unsigned char)*ptr) ? "true" : "false");
ptr++;
}
return 0;
}
Here is the implementation of function isdigit:
int isdigit(char c)
{
return '0' <= c && c <= '9';
}
In simple words, function isdigit takes a char argument as input:
If the input represents a decimal digit in ASCII format, then the function returns 1.
If the input does not represent a decimal digit in ASCII format, then the function returns 0.
When you call function isdigit with an int argument, the argument is first truncated to a char. That doesn't make any difference in your example, since 3 fits into a char, so no information is lost due to the truncation. However, since 3 does not represent any decimal digit in ASCII format, the return-value of isdigit(3) is 0.
To summarize the above:
The return-value of isdigit('0') is 1.
The return-value of isdigit('1') is 1.
...
The return-value of isdigit('9') is 1.
In all other cases, the-return value of isdigit(...) is 0.
Your mistake probably stems from the assumption that '3' == 3. If you want to check whether or not an int variable stores a single-decimal-digit value, then you can implement a slightly different function:
int isintdigit(int i)
{
return 0 <= i && i <= 9;
}

Own strcmp function - non standard chars

I am currently writing a little sort function. I can only use stdio libary, so I wrote my 'own strcmp' function.
int ownstrcmp(char a[], char b[])
{
int i = 0;
while( a[i] == b[i] )
{
if( a[i] == '\0' )
return 0;
++i;
}
return ( a[i] < b[i]) ? 1 : -1;
}
This works great for me. But there is one little problem: What can I do for 'non-Standard-Chars'? Like "ä,ü,ß Their decimal ASCII value is greater than the normal chars, so it sort the string 'example' behind 'ääää'.
I have already read about locale, but the only library that i can use is stdio.h. Is there a 'simple' solution for this problem?
Your question is somewhat vague. First of all, how characters with umlaut are represented depends on your encoding. For example, my computer's locale is set to Greek, meaning that in place of those special Latin characters I have Greek characters. You can't assume anything like that, as far as I can tell.
Second, the answer to your question depends on your representation. Are you still using a "one char per character" representation? If that's so, the above code might still work.
If you're using multi char representation, for example two chars per character, you should change your code so that it exits when two consecutive chars are \0.
Generally, you may want to look into how wchar_t and its family of functions (specifically wcscmp) are implemented.
For german the umlauts ä,ö,ü and ß will be sorted as if they occur in their 'expanded' form:
ä -> ae
ö -> oe
ü -> ue
ß -> ss
In order to get the collation according to the standard you could expand the strings before comparing.
You need to know the encoding the characters are in, and make sure you treat the strings properly. If the encoding is multi-byte, you must start reading (and comparing) individual characters, not bytes.
Also, the way to compare characters internationally varies with the locale, there's no single solution. In some languages, 'ä' sorts after 'z', in some it sorts right next to 'a'.
One simple way of implementing this is of course to create a table which holds the relative order for each character, like so:
unsigned char character_order[256];
character_order[(unsigned char) 'a'] = 1;
character_order[(unsigned char) 'ä'] = character_order[(unsigned char) 'a'];
/* ... and so on ... */
Then instead of subtracting the character's encoded value (which no longer can be used as a "proxy" for the sorting order of the character), you compare the character_order values.
The above assumes single-byte encoding, i.e. Latin-1 or something, since the array size is only 256.
Also note casts to unsigned char when indexing with character literals.
If you are using ISO/IEC_8859-16 encoding, which is the normal enconding for German Language, it's enough to transform your char to unsigned char.
In this way chars can be represented in interval 0-255, suitable for this standard.
Under UTF8 this can help, following your code
if ((a[i] > 0) ^ (b[i] > 0))
return a[i] > 0 ? 1 : -1;
else
return a[i] < b[i] ? 1 : -1;
But you have to check cases like ownstrcmp("ab", "abc");
Furthermore your code doesn't work like strcmp() in <string.h>
A value greater than zero indicates that the first character that does not match has a greater value in str1 than in str2; And a value less than zero indicates the opposite.
I would do it like this:
int ownstrcmp(char a[], char b[])
{
int i = 0;
while(a[i] == b[i]) {
if (a[i] == 0) return 0;
++i;
}
if ((a[i] == 0) || (b[i] == 0))
return a[i] != 0 ? 1 : -1;
if ((a[i] > 0) ^ (b[i] > 0))
return a[i] < 0 ? 1 : -1;
else
return a[i] > b[i] ? 1 : -1;
}

Resources