list convertion in C - c

I am trying to make put command line arguments by the user into an array but I am unsure how to approach it.
For example say I ran my program like this.
./program 1,2,3,4,5
How would I store 1 2 3 4 5 without the commas, and allow it to be passed to other functions to be used. I'm sure this has to do with using argv.
PS: NO space-separated, I want the numbers to parse into integers, I have an array of 200, and I want these numbers to be stored in the array as, arr[0] = 1, arr[1] = 2....
store 1 2 3 4 5 without the commas, and allow it to be passed to other functions to be used.

PS: NO space-separated, I want the numbers to parse into integers
Space or comma-separated doesn't matter. Arguments always come in as strings. You will have to do the work to turn them into integers using atoi (Ascii-TO-Integer).
Using spaces between arguments is the normal convention: ./program 1 2 3 4 5. They come in already separated in argv.
Loop through argv (skipping argv[0], the program name) and run them through atoi.
#include <stdio.h>
#include <stdlib.h>
int main(int argc, char *argv[]) {
for(int i = 1; i < argc; i++) {
int num = atoi(argv[i]);
printf("%d: %d\n", i, num);
}
}
Using commas is going to make that harder. You first have to split the string using the kind of weird strtok (STRing TOKenizer). Then again call atoi on the resulting values.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main(int argc, char *argv[]) {
char *token = strtok(argv[1], ",");
while(token) {
int num = atoi(token);
printf("%d\n", num);
token = strtok(NULL, ",");
}
}
This approach is also more fragile than taking them as individual arguments. If the user types ./program 1, 2, 3, 4, 5 only 1 will be read.

One of the main disadvantages to using atoi() is it provides no check on the string it is processing and will happily accept atoi ("my-cow"); and silently fail returning 0 without any indication of a problem. While a bit more involved, using strtol() allows you to determine what failed, and then recover. This can be as simple or as in-depth a recovery as your design calls for.
As mentioned in the comment, strtol() was designed to work through a string, converting sets of digits found in the string to a numeric value. On each call it will update the endptr parameter to point to the next character in the string after the last digit converted (to each ',' in your case -- or the nul-terminating character at the end). man 3 strtol provides the details.
Since strtol() updates endptr to the character after the last digit converted, you check if nptr == endptr to catch the error when no digits were converted. You check errno for a numeric conversion error such as overflow. Lastly, since the return type is long you need to check if the value returned is within the range of an int before assigning to your int array.
Putting it altogether with a very minimal bit of error handling, you could do something like:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <limits.h>
#include <errno.h>
#define NELEM 200 /* if you need a constant, #define one (or more) */
int main (int argc, char **argv) {
int arr[NELEM] = {0}, ndx = 0; /* array and index */
char *nptr = argv[1], *endptr = nptr; /* nptr and endptr */
if (argc < 2) { /* if no argument, handle error */
fputs ("error: no argument provided.\n", stderr);
return 1;
}
else if (argc > 2) { /* warn on more than 2 arguments */
fputs ("warning: more than one argument provided.\n", stdout);
}
while (ndx < NELEM) { /* loop until all ints processed or arr full */
int error = 0; /* flag indicating error occured */
long tmp = 0; /* temp var to hold strtol return */
char *onerr = NULL; /* pointer to next comma after error */
errno = 0; /* reset errno */
tmp = strtol (nptr, &endptr, 0); /* attempt conversion to long */
if (nptr == endptr) { /* no digits converted */
fputs ("error: no digits converted.\n", stderr);
error = 1;
onerr = strchr (endptr, ',');
}
else if (errno) { /* overflow in conversion */
perror ("strtol conversion error");
error = 1;
onerr = strchr (endptr, ',');
}
else if (tmp < INT_MIN || INT_MAX < tmp) { /* check in range of int */
fputs ("error: value outside range of int.\n", stderr);
error = 1;
onerr = strchr (endptr, ',');
}
if (!error) { /* error flag not set */
arr[ndx++] = tmp; /* assign integer to arr, advance index */
}
else if (onerr) { /* found next ',' update endptr to next ',' */
endptr = onerr;
}
else { /* no next ',' after error, break */
break;
}
/* if at end of string - done, break loop */
if (!*endptr) {
break;
}
nptr = endptr + 1; /* update nptr to 1-past ',' */
}
for (int i = 0; i < ndx; i++) { /* output array content */
printf (" %d", arr[i]);
}
putchar ('\n'); /* tidy up with newline */
}
Example Use/Output
This will handle your normal case, e.g.
$ ./bin/argv1csvints 1,2,3,4,5
1 2 3 4 5
It will warn on bad arguments in list while saving all good arguments in your array:
$ ./bin/argv1csvints 1,my-cow,3,my-cat,5
error: no digits converted.
error: no digits converted.
1 3 5
As well as handling completely bad input:
$ ./bin/argv1csvints my-cow
error: no digits converted.
Or no argument at all:
$ ./bin/argv1csvints
error: no argument provided.
Or more than the expected 1 argument:
$ ./bin/argv1csvints 1,2,3,4,5 6,7,8
warning: more than one argument provided.
1 2 3 4 5
The point to be made it that with a little extra code, you can make your argument parsing routine as robust as need be. While your use of a single argument with comma-separated values is unusual, it is doable. Either manually tokenizing (splitting) the number on the commas with strtok() (or strchr() or combination of strspn() and strcspn()), looping with sscanf() using something similar to the "%d%n" format string to get a minimal succeed / fail indication with the offset of the next number from the last, or using strtol() and taking advantage of its error reporting. It's up to you.
Look things over and let me know if you have questions.

This is how I'd deal with your requirement using strtol(). This does not damage the input string, unlike solutions using strtok(). It also handles overflows and underflows correctly, unlike solutions using atoi() or its relatives. The code assumes you want to store an array of type long; if you want to use int, you can add testing to see if the value converted is larger than INT_MAX or less than INT_MIN and report an appropriate error if it is not a valid int value.
Note that handling errors from strtol() is a tricky business, not least because every return value (from LONG_MIN up to LONG_MAX) is also a valid result. See also Correct usage of strtol(). This code requires no spaces before the comma; it permits them after the comma (so you could run ./csa43 '1, 2, -3, 4, 5' and it would work). It does not allow spaces before commas. It allows leading spaces, but not trailing spaces. These issues could be fixed with more work — probably mostly in the read_value() function. It may be that the validation work in the main loop should be delegated to the read_value() function — it would give a better separation of duty. OTOH, what's here works within limits. It would be feasible to allow trailing spaces, or spaces before commas, if that's what you choose. It would be equally feasible to prohibit leading spaces and spaces after commas, if that's what you choose.
#include <errno.h>
#include <limits.h>
#include <stdio.h>
#include <stdlib.h>
static int read_val(const char *str, char **eov, long *value)
{
errno = 0;
char *eon;
if (*str == '\0')
return -1;
long val = strtol(str, &eon, 0);
if (eon == str || (*eon != '\0' && *eon != ',') ||
((val == LONG_MIN || val == LONG_MAX) && errno == ERANGE))
{
fprintf(stderr, "Could not convert '%s' to an integer "
"(the leftover string is '%s')\n", str, eon);
return -1;
}
*value = val;
*eov = eon;
return 0;
}
int main(int argc, char **argv)
{
if (argc != 2)
{
fprintf(stderr, "Usage: %s n1,n2,n3,...\n", argv[0]);
exit(EXIT_FAILURE);
}
enum { NUM_ARRAY = 200 };
long array[NUM_ARRAY];
size_t nvals = 0;
char *str = argv[1];
char *eon;
long val;
while (read_val(str, &eon, &val) == 0 && nvals < NUM_ARRAY)
{
array[nvals++] = val;
str = eon;
if (str[0] == ',' && str[1] == '\0')
{
fprintf(stderr, "%s: trailing comma in number string\n", argv[1]);
exit(EXIT_FAILURE);
}
else if (str[0] == ',')
str++;
}
for (size_t i = 0; i < nvals; i++)
printf("[%zu] = %ld\n", i, array[i]);
return 0;
}
Output (program csa43 compiled from csa43.c):
$ csa43 1,2,3,4,5
[0] = 1
[1] = 2
[2] = 3
[3] = 4
[4] = 5
$

Related

C if statement, optimal way to check for special characters and letters

Hi folks thanks in advance for any help, I'm doing the CS50 course i'm at the very beginning of programming.
I'm trying to check if the string from the main function parameter string argv[] is indeed a number, I searched multiple ways.
I found in another topic How can I check if a string has special characters in C++ effectively?, on the solution posted by the user Jerry Coffin:
char junk;
if (sscanf(str, "%*[A-Za-z0-9_]%c", &junk))
/* it has at least one "special" character
else
/* no special characters */
if seems to me it may work for what I'm trying to do, I'm not familiar with the sscanf function, I'm having a hard time, to integrate and adapt to my code, I came this far I can't understand the logic of my mistake:
#include <cs50.h>
#include <stdio.h>
#include <stdlib.h>
#include <ctype.h>
#include <string.h>
int numCheck(string[]);
int main(int argc, string argv[]) {
//Function to check for user "cooperation"
int key = numCheck(argv);
}
int numCheck(string input[]) {
int i = 0;
char junk;
bool usrCooperation = true;
//check for user "cooperation" check that key isn't a letter or special sign
while (input[i] != NULL) {
if (sscanf(*input, "%*[A-Za-z_]%c", &junk)) {
printf("test fail");
usrCooperation = false;
} else {
printf("test pass");
}
i++;
}
return 0;
}
check if the string from the main function parameter string argv[] is indeed a number
A direct way to test if the string converts to an int is to use strtol(). This nicely handles "123", "-123", "+123", "1234567890123", "x", "123x", "".
int numCheck(const char *s) {
char *endptr;
errno = 0; // Clear error indicator
long num = strtol(s, &endptr, 0);
if (s == endptr) return 0; // no conversion
if (*endptr) return 0; // Junk after the number
if (errno) return 0; // Overflow
if (num > INT_MAX || num < INT_MIN) return 0; // int Overflow
return 1; // Success
}
int main(int argc, string argv[]) {
// Call each arg[] starting with `argv[1]`
for (int a = 1; a < argc; a++) {
int success = numCheck(argv[a]);
printf("test %s\n", success ? "pass" : "fail");
}
}
sscanf(*input, "%*[A-Za-z_]%c", &junk) is the wrong approach for testing numerical conversion.
You pass argv to numcheck and test all strings in it: this is incorrect as argv[0] is the name of the running executable, so you should skip this argument. Note also that you should pass input[i] to sscanf(), not *input.
Furthermore, lets analyze the return value of sscanf(input[i], "%*[A-Za-z_]%c", &junk):
it returns EOF if the input string is empty,
it returns 0 if %*[A-Za-z_] fails,
it also returns 0 if the conversion %c fails after the %*[A-Za-z_] succeeds,
it returns 1 is both conversions succeed.
This test is insufficient to check for non digits in the string, it does not actually give useful information: the return value will be 0 for the string "1" and also for the string "a"...
sscanf() is very tricky, full of quirks and traps. Definitely not the right tool for pattern matching.
If the goal is to check that the strings contain only digits (at least one), use this instead, using the often overlooked standard function strspn():
#include <stdio.h>
#include <string.h>
int numCheck(char *input[]) {
int i;
int usrCooperation = 1;
//check for user "cooperation" check that key isn't a letter or special sign
for (i = 1; input[i] != NULL; i++) {
// count the number of matching character at the beginning of the string
int ndigits = strspn(input[i], "0123456789");
// check for at least 1 digit and no characters after the digits
if (ndigits > 0 && input[i][ndigits] == '\0') {
printf("test passes: %d digits\n", ndigits);
} else {
printf("test fails\n");
usrCooperation = 0;
}
}
return usrCooperation;
}
Let's try this again:
This is still your problem:
if (sscanf(*input, "%*[A-Za-z_]%c", &junk))
but not for the reason I originally said - *input is equal to input[0]. What you want to have there is
if ( sscanf( input[i], "%*[A-Za-z_]%c", &junk ) )
what you're doing is cycling through all your command line arguments in the while loop:
while( input[i] != NULL )
but you're only actually testing input[0].
So, quick primer on sscanf:
The first argument (input) is the string you're scanning. The type of this argument needs to be char * (pointer to char). The string typedef name is an alias for char *. CS50 tries to paper over the grosser parts of C string handling and I/O and the string typedef is part of that, but it's unique to the CS50 course and not a part of the language. Beware.
The second argument is the format string. %[ and %c are format specifiers and tell sscanf what you're looking for in the string. %[ specifies a set of characters called a scanset - %[A-Za-z_] means "match any sequence of upper- and lowercase letters and underscores". The * in %*[A-Za-z_] means don't assign the result of the scan to an argument. %c matches any character.
Remaining arguments are the input items you want to store, and their type must match up with the format specifier. %[ expects its corresponding argument to have type char * and be the address of an array into which the input will be stored. %c expects its corresponding argument (in this case junk) to also have type char *, but it's expecting the address of a single char object.
sscanf returns the number of items successfully read and assigned - in this case, you're expecting the return value to be either 0 or 1 (because only junk gets assigned to).
Putting it all together,
sscanf( input, "%*[A-Za-z_]%c", &junk )
will read and discard characters from input up until it either sees the string terminator or a character that is not part of the scanset. If it sees a character that is not part of the scanset (such as a digit), that character gets written to junk and sscanf returns 1, which in this context is treated as "true". If it doesn't see any characters outside of the scanset, then nothing gets written to junk and sscanf returns 0, which is treated as "false".
EDIT
So, chqrlie pointed out a big error of mine - this test won't work as intended.
If there are no non-letter and non-underscore characters in input[i], then nothing gets assigned to junk and sscanf returns 0 (nothing assigned). If input[i] starts with a letter or underscore but contains a non-letter or non-underscore character later on, that bad character will be converted and assigned to junk and sscanf will return 1.
So far so good, that's what you want to happen. But...
If input[i] starts with a non-letter or non-underscore character, then you have a matching failure and sscanf bails out, returning 0. So it will erroneously match a bad input.
Frankly, this is not a very good way to test for the presence of "bad" characters.
A potentially better way would be to use something like this:
while ( input[i] )
{
bool good = true;
/**
* Cycle through each character in input[i] and
* check to see if it's a letter or an underscore;
* if it isn't, we set good to false and break out of
* the loop.
*/
for ( char *c = input[i]; *c; c++ )
{
if ( !isalpha( *c ) && *c != '_' )
{
good = false;
break;
}
}
if ( !good )
{
puts( "test fails" );
usrCooperation = 0;
}
else
{
puts( "test passes" );
}
}
I followed the solution by the user "chux - Reinstate Monica". thaks everybody for helping me solve this problem. Here is my final program, maybe it can help another learner in the future. I decided to avoid using the non standard library "cs50.h".
//#include <cs50.h>
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <limits.h>
void keyCheck(int);
int numCheck(char*);
int main(int argc, char* argv[])
{
//Error code == 1;
int key = 0;
keyCheck(argc); //check that two parameters where sent to main.
key = numCheck(argv[1]); //Check for user "cooperation".
return 0;
}
//check for that main received two parameters.
void keyCheck(int key)
{
if (key != 2) //check that main argc only has two parameter. if not terminate program.
{
exit(1);
}
}
//check that the key (main parameter (argv [])) is a valid number.
int numCheck(char* input)
{
char* endptr;
errno = 0;
long num = strtol(input, &endptr, 0);
if (input == endptr) //no conversion is possible.
{
printf("Error: No conversion possible");
return 1;
}
else if (errno == ERANGE) //Input out of range
{
printf("Error: Input out of range");
return 1;
}
else if (*endptr) //Junk after numeric text
{
printf("Error: data after main parameter");
return 1;
}
else //conversion succesfull
{
//verify that the long int is in the integer limits.
if (num >= INT_MIN && num <= INT_MAX)
{
return num;
}
//if the main parameter is bigger than an int, terminate program
else
{
printf("Error key out of integer limits");
exit(1);
}
}
/* else
{
printf("Success: %ld", num);
return num;
} */
}

Determine number or character in textfile C [closed]

Closed. This question needs to be more focused. It is not currently accepting answers.
Want to improve this question? Update the question so it focuses on one problem only by editing this post.
Closed 3 years ago.
Improve this question
I have a textfile with these following numbers and characters inside of it.
36#xL!?\8
28?>\4
42<pX%7
37##5
31kL%^?>\<#%5
Now, i want to get the first integer which is 36 and subtract it on the last integer which is 8. I want to do this line by line.
You want to read in the line, parse the numbers at the beginning and end, then convert them to integers. Here is a simple example:
#include <stdio.h>
#include <ctype.h>
#include <stdlib.h>
#include <string.h>
main()
{
FILE *file = fopen("input.txt", "r");
char line[256];
while (fgets(line, sizeof(line), file))
{
char num[15];
int firstNumber = 0;
int secondNumber = 0;
line[strcspn(line, "\r\n")] = 0;
for (int x = 0; x < 256; x++)
{
if (isdigit(line[x]))
{
num[x] = line[x];
}
else
{
num[x] = 0;
break;
}
}
firstNumber = atoi(num);
int length = strlen(line);
int ndx = 0;
while (length >=0 && isdigit(line[length - 1]))
{
num[ndx] = line[length - 1];
ndx++;
length--;
}
num[ndx] = 0;
secondNumber = atoi(num);
printf("%d - %d = %d\n", firstNumber, secondNumber, firstNumber - secondNumber);
}
fclose(file);
}
You already have a good answer for the file posted as part of your question, however, the result of first - last will be incorrect if you line of text contains a '-' sign before the digits indicating a negative signed value. All of the C string to integer conversions will accept a leading +/- before the value to be converted indicating a positive or negative number. If your input can contain negative values, you need to preserve the '-' sign by including that with the digits to be converted. For example if your input file was:
36#xL!?\-8
28?>\4
-42<pX%7
37##-5
31kL%^?>\<#%5
The answers would be quite different with -8, -42 and -5 as the integer values from the file. Now if that isn't a possibility based on your specific assignment, then you can skip preserving the '-', but for read-world text conversion to signed values, it is critical.
One way of finding the beginning of a signed number for conversion in a string is simply scan forward in the string (similar to what strpbrk() would do) looking for either a beginning digit or '-' (whichever occurs first). If the '-' occurs first, you then check that the next character is a digit. You can do it with a simple loop, e.g.
/** scan forward in 'p' to find beginning of next valid signed integer.
* returns pointer to beginning of signed int on success, NULL otherwise.
*/
const char *nextdigit (const char *p)
{
while (*p) {
/* if digit or +/- followed by a digit */
if (isdigit(*p) ||
((*p == '-' || *p == '+') && isdigit(*(p + 1))))
return p;
p++;
}
return NULL;
}
Once you have found the beginning of a digit, then you need to use a function that provides error-checking on the conversion. atoi() provides zero diagnostics for the conversion and will silently return 0 as a valid number for the conversion of atoi("my cow"); You will have no indication whether digits were actually converted or whether the result exceeds the storage size for all integer types. atoi() issues no errors at all, even if provided with a 200 digit string as input. At minimum, use sscanf that will at least provide a yes/no, true/false as to whether a valid conversion took place, or better, use strtol which provides full error reporting on the conversion.
For example, you can write a short function that take the address of a pointer to string, use nextdigit() function above, and then use strtol to fully validate the result, setting errno for validation back in the caller on any error and returning the results of the integer conversion (or 0 on error) as follows:
/** returns next integer in string pointed to by p, or sets errno and returns
* zero on error.
*/
int getnextint (char **p)
{
int nextint = 0;
errno = 0;
if ((*p = (char*)nextdigit(*p))) {
char *endptr;
long tmp = strtol (*p, &endptr, 0);
if (*p == endptr) { /* validate digits converted */
fputs ("error: no digits converted.\n", stderr);
errno = EINVAL;
}
else if (errno) /* validate conversion */
fputs ("error: over/underflow occurred.\n", stderr);
/* validate tmp is in range of integer */
else if (INT_MIN <= tmp && tmp <= INT_MAX)
nextint = tmp;
else {
fputs ("error: value exceeds range of int.\n", stderr);
errno = ERANGE;
}
*p = (char*)nextdigit(endptr);
}
else
errno = EINVAL; /* if no digits found, set EINVAL */
return nextint;
}
(note: the address of the pointer is passed so that the pointer can be updated within the function to the beginning of the next integer to convert in the string (or NULL if no more remain)
To complete the example, you can add the needed headers and write a short main() to read from your filename provided as the first argument (or read from stdin by default if no argument is provided) that will locate the first and last integers in each line and subtract first - last outputting the result:
#include <stdio.h>
#include <stdlib.h> /* for strtol */
#include <string.h> /* for strcspn */
#include <limits.h> /* for INT_MIN/INT_MAX */
#include <errno.h> /* for errno */
#include <ctype.h> /* for isdigit */
#define ARSZ 100
#define MAXC 1024
... /* insert functions here */
int main (int argc, char **argv) {
char buf[MAXC] = "";
/* use filename provided as 1st argument (stdin by default) */
FILE *fp = argc > 1 ? fopen (argv[1], "r") : stdin;
if (!fp) { /* validate file open for reading */
perror ("file open failed");
return 1;
}
while (fgets (buf, MAXC, fp)) { /* read each line of input */
int arr[ARSZ] = {0};
char *p = buf;
size_t n = 0;
buf[strcspn(buf, "\r\n")] = 0;
while (n < ARSZ && p) {
arr[n] = getnextint (&p);
if (!errno)
n++;
}
if (n > 1)
printf ("%-19s : % 2d - % 2d = % 3d\n",
buf, *arr, arr[n-1], *arr - arr[n-1]);
else
fprintf (stderr, "%zu integer(s) in: '%s'\n", n, buf);
}
if (fp != stdin) /* close file if not stdin */
fclose (fp);
}
Example Input Files
Your original input file:
$ cat dat/last-first.txt
36#xL!?\8
28?>\4
42<pX%7
37##5
31kL%^?>\<#%5
And another with negative values and additional extraneous lines:
$ cat dat/last-first2.txt
36#xL!?\-8
28?>\4
-42<pX%7
Nothing to see!
37##-5
31kL%^?>\<#%5
Example Use/Output
$ ./bin/fgets_strtol_any_last-first dat/last-first.txt
36#xL!?\8 : 36 - 8 = 28
28?>\4 : 28 - 4 = 24
42<pX%7 : 42 - 7 = 35
37##5 : 37 - 5 = 32
31kL%^?>\<#%5 : 31 - 5 = 26
When run on the file with negative values and the extraneous line:
$ ./bin/fgets_strtol_any_last-first dat/last-first2.txt
36#xL!?\-8 : 36 - -8 = 44
28?>\4 : 28 - 4 = 24
-42<pX%7 : -42 - 7 = -49
0 integer(s) in: 'Nothing to see!'
37##-5 : 37 - -5 = 42
31kL%^?>\<#%5 : 31 - 5 = 26
As you can see from the result of the subtractions between the different files, it makes a great deal of difference whether you preserve the leading '-' when converting signed values. Something to consider going forward.
Look things over and let me know if you have additional questions.

C Format Specifier Sizes

How do I access an integer value in a character array?
char a = 'A';
int b = 90;
char * c = "A String";
snprintf(output_buffer, 1024, "%c%d%s, a,b,c);`
How many bytes will the %d format specifier (I assume 4?) take up in this character array and how do I access the integer value b?
I've tried:
int x = *((int *) &output_buffer[1]
without success.
short x;
sscanf(&output_buffer[1], "%d", &x);
printf("%d\n",x);`
char *buffer;
sscanf(&output_buffer[3], "%s", buffer);
printf("%s\n",buffer);`
In answer to your original question, "how many characters will "%d" take?", you can use a trick with snprintf by specifying the buffer as NULL and the number of characters as 0, and then using your "%d" format string and your variable b, snprintf will return the number of digits the conversion will require, e.g.
req = snprintf (NULL, 0, "%d", b);
printf ("required digits: %d\n", req);
Which will output "required digits: 2". ("the number of characters (excluding the terminating null byte) which would have been written to the final string if enough space had been available.") Which is useful when dynamically allocating storage for buffer. In fact, you simply provide your full format string and all variables and snprintf will return the total number of characters needed (to which you add +1 for the nul-terminating character)
From the last few comments, I take it you want to read 90 back into an int from within buffer. That is simple enough to do.
Rather than simply attempting to convert by using the 2nd character in buffer (e.g. buffer[1]), for the generic case, you simply want to start with the 1st character in buffer and scan forward until you find the first digit. (you can also check for '+/-' if you have explicit signed values).
To scan forward in buffer to find the first digit, you iterate over the characters in buffer (either using indexes, e.g. buffer[x], or using a pointer, e.g, char *p = buffer; and incrementing p++) and check whether each character is a digit. While you can simply use if ('0' <= *p && *p <= '9'), the ctype.h header provides the isdigit() macro that makes this quite easy. To find the first digit, you could do something like:
#include <ctype.h> /* for isdigit */
...
char buffer[MAXC] = "", /* buffer to hold a, b, c */
*p = buffer;
...
while (*p && !isdigit(*p)) /* scan forward in buffer to 1st digit */
p++;
Once you have found your first digit, you convert the sequence of digits to a long value using strtol (which provides full error checking), e.g.
#include <stdlib.h>
#include <errno.h> /* for errno */
#include <limits.h> /* for INT_MIN/INT_MAX */
...
char *endptr; /* end pointer to use with strtol */
long tmp; /* long value for return of strtol */
...
errno = 0; /* reset errno - to check after strtol */
tmp = strtol (p, &endptr, 0); /* save conversion result in tmp */
(note: avoid atoi() is provides zero error checking of the conversion)
Now tmp holds the return of strtol which will contain a conversion to long of the digits found in buffer beginning at p (on success). But, before you can make use of the value returned as an int, you must validate that digits were converted, that no error occurred within the conversion, and that the value in tmp is within the range of int. You can do all with a few conditional checks. If all your checks are satisfied, you can then assign the value in tmp to an integer (with the appropriate cast) and have confidence in your final value, e.g.
if (p == endptr) /* check if pointer == end pointer */
fputs ("error: no digits converted.\n", stderr);
else if (errno) /* if errno set, over/underflow occurred */
fputs ("error: invalid conversion to long.\n", stderr);
else if (tmp < INT_MIN || INT_MAX < tmp) /* will fit in int? */
fputs ("error: value exceeds range of int.\n", stderr);
else { /* good value, assign to b_from_buf, output */
b_from_buf = (int)tmp;
printf ("\nint read from buffer: %d\n", b_from_buf);
}
Putting your example together (and including validation of your original write to buffer with snprintf, you could do something similar to the following):
#include <stdio.h>
#include <stdlib.h>
#include <ctype.h> /* for isdigit */
#include <errno.h> /* for errno */
#include <limits.h> /* for INT_MIN/INT_MAX */
#define MAXC 1024
int main (void) {
char a = 'A',
*c = "A String",
buffer[MAXC] = "", /* buffer to hold a, b, c */
*p = buffer, /* pointer to buffer */
*endptr; /* end pointer to use with strtol */
int b = 90,
b_from_buf, /* int to read from filled buffer */
rtn; /* return for snprintf to validate */
long tmp; /* long value for return of strtol */
rtn = snprintf (buffer, MAXC, "%c%d%s", a, b, c);
if (rtn < 0) { /* if < 0, error occurred */
fputs ("error: writing to buffer.\n", stderr);
return 1;
}
else if (rtn >= MAXC) /* if > size, truncation occurred */
fputs ("warning: buffer contains truncated string.\n", stderr);
printf ("%s\n", buffer); /* output buffer */
while (*p && !isdigit(*p)) /* scan forward in buffer to 1st digit */
p++;
errno = 0; /* reset errno - to check after strtol */
tmp = strtol (p, &endptr, 0); /* save conversion result in tmp */
if (p == endptr) /* check if pointer == end pointer */
fputs ("error: no digits converted.\n", stderr);
else if (errno) /* if errno set, over/underflow occurred */
fputs ("error: invalid conversion to long.\n", stderr);
else if (tmp < INT_MIN || INT_MAX < tmp) /* will fit in int? */
fputs ("error: value exceeds range of int.\n", stderr);
else { /* good value, assign to b_from_buf, output */
b_from_buf = (int)tmp;
printf ("\nint read from buffer: %d\n", b_from_buf);
}
}
(note: if the value in buffer can have an explicit sign before it, e.g. '-' or '+', then you add those to the same conditional with isdigit())
Example Use/Output
$ ./bin/snprintf_string
A90A String
int read from buffer: 90
After Last Comment Wanting 'a, b & c` Back
You already have all you need to get a, b & c back from buffer. Since you used strtol and endptr will point to the next character after the last digit converted, you can get a, b & c back from buffer by simply outputting the values, e.g.
else { /* good value, assign to b_from_buf, output */
b_from_buf = (int)tmp;
printf ("\n(a) 1st char in buffer : %c\n"
"(b) int read from buffer : %d\n"
"(c) remaining chars in buffer : %s\n",
*buffer, b_from_buf, endptr);
}
Modified Example Use/Output
$ ./bin/snprintf_string
A90A String
(a) 1st char in buffer : A
(b) int read from buffer : 90
(c) remaining chars in buffer : A String
Look things over and let me know if you have further questions.
There exists a %n modifiers which stores the actual number of written bytes into an int:
int main(int argc, char *argv[])
{
int p0;
int p1;
char buf[128];
sprintf(buf, "%c%n%d%n%s", argv[0][0], &p0, atoi(argv[1]), &p1, argv[1]);
printf("'%s' -> %d, %d\n", buf, p0, p1);
}
But this modifier is considered dangerous; some implementations require that the format string is located in read-only memory.
To make the last sentence more clear, an example:
#include <stdio.h>
int main(void)
{
char fmt[] = "%n";
int pos;
printf("%n", &pos);
printf("ok\n");
printf(fmt, &pos);
printf("ok\n");
}
and then
$ gcc x.c -D_FORTIFY_SOURCE=2 -O2
$ ./a.out
ok
*** %n in writable segment detected ***
Aborted (core dumped)
In your example, assuming the character array you are asking about is output_buffer and that the size of char is 1 byte in your architecture, the %d will take 2 bytes, one for each digit of your int (b = 90). To get back the value, use:
int x;
sscanf(&output_buffer[1], "%d", &x);
char buffer[255];
sscanf(&output_buffer[3], "%[^\n]", buffer);
Please, check the size of buffer to avoid overflows

How to convert a string say of type AA:BB:CC:DD:EE:FF to 0xaabbccddeeff in C? [closed]

Closed. This question needs debugging details. It is not currently accepting answers.
Edit the question to include desired behavior, a specific problem or error, and the shortest code necessary to reproduce the problem. This will help others answer the question.
Closed 4 years ago.
Improve this question
Input: AA:BB:CC:DD:EE:FF Output expected: 0xaabbccddeeff.
Input: AA:BB:65:F0:E4:D4 Output expected:0xaabb65f0e4d4
char arr[20]="AA:BB:CC:DD:EE:FF";
char t[20]="0x";
char *token=strtok(arr[i], ":");
while(token !=NULL){
printf("%s\n", token);
token = strtok(NULL, ":");
strcat(t, token);
}
printf("The modified string is %s\n", t);
I am seeing a segmentation fault.
You're attempting the final strcat with a null token. Try moving your conditional to check for that before making the strcat call:
#include <ctype.h>
#include <stdio.h>
#include <string.h>
void lower(char *c) {
for (; *c = tolower(*c); *c++);
}
int main() {
char s[] = "AA:BB:CC:DD:EE:FF";
char t[15] = "0x";
char *token = strtok(s, ":");
if (token) {
lower(token);
strcat(t, token);
while (token = strtok(NULL, ":")) {
lower(token);
strcat(t, token);
}
}
printf("The modified string is %s\n", t);
}
Output:
The modified string is 0xaabbccddeeff
Use the 64-bit unsigned integer type uint64_t (declared in <inttypes.h>) to store the 48-bit value (HH:HH:HH:HH:HH:HH → 0xHHHHHHHHHHHH).
You could use sscanf(), but it does not detect overflow; it would consider only the two rightmost hexadecimal characters in each part, so F11:E22:D33:C44:B55:A66 would yield the same result as 11:22:33:44:55:66.
First, we need a function to convert a hexadecimal digit to its numerical value. Here is the simplest, most easy to read, and also most portable way to write it:
#include <stdlib.h>
#include <string.h>
#include <inttypes.h>
#include <ctype.h>
#include <stdio.h>
static inline int hex_digit(const int c)
{
switch (c) {
case '0': return 0;
case '1': return 1;
case '2': return 2;
case '3': return 3;
case '4': return 4;
case '5': return 5;
case '6': return 6;
case '7': return 7;
case '8': return 8;
case '9': return 9;
case 'A': case 'a': return 10;
case 'B': case 'b': return 11;
case 'C': case 'c': return 12;
case 'D': case 'd': return 13;
case 'E': case 'e': return 14;
case 'F': case 'f': return 15;
default: return -1;
}
}
The function will return a nonnegative (0 or positive) integer corresponding to the character, or -1 if the character is not a hexadecimal digit.
The static inline means that the function is only visible in this translation unit (file; or if put in a header file, each file that #includes that header file). It was standardized in C99 as a way for programmers to write functions that are as fast as (incur no runtime overhead compared to) preprocessor macros.
Next, we need a function to carefully parse the string. Here is one:
/* Parse a string "HH:HH:HH:HH:HH:HH" to 0x00HHHHHHHHHHHH,
and return a pointer to the character following it,
or NULL if an error occurs. */
static const char *parse_mac(const char *src, uint64_t *dst)
{
uint64_t value = 0;
int i, hi, lo;
/* No string specified? */
if (!src)
return NULL;
/* Skip leading whitespace. */
while (isspace((unsigned char)(*src)))
src++;
/* End of string? */
if (!*src)
return NULL;
/* First pair of hex digits. */
if ((hi = hex_digit(src[0])) < 0 ||
(lo = hex_digit(src[1])) < 0)
return NULL;
value = 16*hi + lo;
src += 2;
/* The next five ":HH" */
for (i = 0; i < 5; i++) {
if (src[0] != ':' || (hi = hex_digit(src[1])) < 0 ||
(lo = hex_digit(src[2])) < 0 )
return NULL;
value = 256*value + 16*hi + lo;
src += 3;
}
/* Successfully parsed. */
if (dst)
*dst = value;
return src;
}
Above, we marked the function static, meaning it too is only visible in this compilation unit. It is not marked inline, because it is not a trivial function; it does proper work, so we do not suggest the compiler should inline it.
Note the cast to unsigned char in the isspace() call. This is because isspace() takes either an unsigned char, or EOF. If we supply it a char, and char type happens to be a signed type (it varies between architectures), some characters do get incorrectly classified. So, using the cast with the character-type functions (isspace(), isblank(), tolower(), `toupper(), et cetera) is important, if you want your code to work right on all systems that support standard C.
You might not be familiar with the idiom if ((variable = subexpression) < 0). For each (variable = subexpression) < 0, the subexpression gets evaluated, then assigned to the variable. If the value is less than zero, the entire expression is true; otherwise it is false. The variable will retain its new value afterwards.
In C, logical AND (&&) and OR (||) are short-circuiting. This means that if you have A && B, and A is false, then B is not evaluated at all. If you have A || B, and A is true, then B is not evaluated at all. So, in the above code,
if ((hi = hex_digit(src[0])) < 0 ||
(lo = hex_digit(src[1])) < 0)
return NULL;
is exactly equivalent to
hi = hex_digit(src[0]);
if (hi < 0)
return NULL;
lo = hex_digit(src[1]);
if (lo < 0)
return NULL;
Here, we could have written those two complicated if statements more verbosely, but I wanted to include it in this example, to make this answer into something you must "chew" a bit in your mind, before you can use it in e.g. homework.
The main "trick" in the function is that we build value by shifting its digits leftward. If we are parsing 12:34:56:78:9A:BC, the first assignment to value is equivalent to value = 0x12;. Multiplying value by 256 shifts the hexadecimal digits by two places (because 256 = 0x100), so in the first iteration of the for loop, the assignment to value is equivalent to value = 0x1200 + 0x30 + 0x4; i.e. value = 0x1234;. This goes on for four more assignments, so that the final value is 0x123456789ABC;. This "shifting digits via multiplication" is very common, and works in all numerical bases (for decimal numbers, the multiplier is a power of 10; for octal numbers, a power of 8; for hexadecimal numbers, a power of 16; always a power of the base).
You can, for example, use this approach to reverse the digits in a number (so that one function converts 0x123456 to 0x654321, and another converts 8040201 to 1020408).
To test the above, we need a main(), of course. I like my example programs to tell me what they do if I run them without arguments. When they work on strings or numbers, I like to provide them on the command line, rather than having the program ask for input:
int main(int argc, char *argv[])
{
const char *end;
uint64_t mac;
int arg;
if (argc < 2 || !strcmp(argv[1], "-h") || !strcmp(argv[1], "--help")) {
fprintf(stderr, "\n");
fprintf(stderr, "Usage: %s [ -h | --help ]\n", argv[0]);
fprintf(stderr, " %s HH:HH:HH:HH:HH:HH ...\n", argv[0]);
fprintf(stderr, "\n");
fprintf(stderr, "This program parses the hexadecimal string(s),\n");
fprintf(stderr, "and outputs them in both hexadecimal and decimal.\n");
fprintf(stderr, "\n");
return EXIT_FAILURE;
}
for (arg = 1; arg < argc; arg++) {
end = parse_mac(argv[arg], &mac);
if (!end) {
fprintf(stderr, "Cannot parse '%s'.\n", argv[arg]);
return EXIT_FAILURE;
}
if (*end)
printf("%s: 0x%012" PRIx64 " = %" PRIu64 " in decimal; '%s' unparsed.\n",
argv[arg], mac, mac, end);
else
printf("%s: 0x%012" PRIx64 " = %" PRIu64 " in decimal.\n",
argv[arg], mac, mac);
fflush(stdout);
}
return EXIT_SUCCESS;
}
The first if clause checks if there are any command-line parameters. (argv[0] is the program name itself, and is included in argc, the number of strings in argv[] array. In other words, argc == 1 means only the program name was supplied on the command line, argc == 2 means the program name and one parameter (in argv[1]) was supplied, and so on.)
Because it is often nice to supply more than one item to work on, we have a for loop over all command-line parameters; from argv[1] to argv[argc-1], inclusive. (Remember, because argc is the number of strings in the argv[] array, and numbering starts from 0, the last is argc-1. This is important to remember in C, in all array use!)
Within the for loop, we use our parse function. Because it returns a pointer to the string following the part we parsed, and we store that to end, (*end == '\0') (which is equivalent to the shorter form (!*end) is true if the string ended there. If (*end) (equivalent to (*end != '\0')) is true, then there are additional characters in the string following the parsed part.
To output any of the integer types specified in <inttypes.h>, we must use preprocessor macros. For uint64_t, we can use "%" PRIu64 to print one in decimal; or "%" PRIx64 to print one in hexadecimal. "%012" PRIu64 means "Print a 12-digit uint64_t, zero-padded (on the left)".
Remember that in C, string literals are concatenated; "a b", "a " "b", "a" " " "b" are all equivalent. (So, the PRI?## macros all expand to strings that specify the exact conversion type. They are macros, because they vary between systems. In 64-bit Windows PRIu64 is usually "llu", but in 64-bit Linux it is "lu".)
The fflush(stdout); at the end should do nothing, because standard output is by default line buffered. However, because I explicitly want the C library to ensure the output is output to standard output before next loop iteration, I added it. It would matter if one changed standard output to fully buffered. As it is, it is an "insurance" (against oddly behaving C library implementations), and a reminder to us human programmers that the intent is to have the output flushed, not cached by the C library, at that point.
(Why do we want that? Because if an error occurs during the next iteration, and we print errors to standard error, and standard output and error are both usually directed to the terminal, we want the standard output to be visible before the standard error is, to avoid user confusion.)
If you compile the above to say example (I use Linux, so I run it as ./example; in Windows, you probably run it as example.exe), you can expect the following outputs:
./example 12:34:56:07:08:09 00:00:00:00:00:00foo bad
12:34:56:07:08:09: 0x123456070809 = 20015990900745 in decimal.
00:00:00:00:00:00foo: 0x000000000000 = 0 in decimal; 'foo' unparsed.
Cannot parse 'bad'.
If you run it without parameters, or with just -h or --help, you should see
Usage: ./z [ -h | --help ]
./z HH:HH:HH:HH:HH:HH ...
This program parses the hexadecimal string(s),
and outputs them in both hexadecimal and decimal.
Obviously, there are other ways to achieve the same. If you are only interested in the string representation, you could use e.g.
#include <stdlib.h>
#include <ctype.h>
char *mac_to_hex(const char *src)
{
char *dst, *end;
int i;
if (!src)
return NULL;
/* Skip leading whitespace. */
while (isspace((unsigned char)(*src)))
src++;
/* The next two characters must be hex digits. */
if (!isxdigit((unsigned char)(src[0])) ||
!isxdigit((unsigned char)(src[1])))
return NULL;
/* Dynamically allocate memory for the result string.
"0x112233445566" + '\0' = 15 chars total. */
dst = malloc(15);
if (!dst)
return NULL;
/* Let end signify the position of the next char. */
end = dst;
/* Prefix, and the first two hex digits. */
*(end++) = '0';
*(end++) = 'x';
*(end++) = *(src++);
*(end++) = *(src++);
/* Loop over the five ":HH" parts left. */
for (i = 0; i < 5; i++) {
if (src[0] == ':' &&
isxdigit((unsigned char)(src[1])) &&
isxdigit((unsigned char)(src[2])) ) {
*(end++) = src[1];
*(end++) = src[2];
src += 3;
} else {
free(dst);
return NULL;
}
}
/* All strings need a terminating '\0' at end.
We allocated enough room for it too. */
*end = '\0';
/* Ignore trailing whitespace in source string. */
while (isspace((unsigned char)(*src)))
src++;
/* All of source string processed? */
if (*src) {
/* The source string contains more stuff; fail. */
free(dst);
return NULL;
}
/* Success! */
return dst;
}
I consider this approach much less useful, because the source string must contain exactly HH:HH:HH:HH:HH:HH (although leading and trailing whitespace is allowed). Parsing it to an unsigned integer lets you e.g. read a line, and parse all such patterns on it, with a simple loop.
If you find any bugs or issues in the above, let me know in a comment so I can verify and fix if necessary.

Convert hexadecimal numbers to decimal numbers

I have a text file with one line with numbers separated by space as in the following example:
1 -2 3.1 0xf 0xcc
After parsing the file with a C program,the results should be saved in a vector and its internal structure should be:
V[0]=1
V[1]=-2
V[2]=3.1
V[3]=15
V[4]=204
Basically i need to convert the numbers that start with 0x into decimal numbers.
I have tried storing all elements in a char vector and then transform them in numbers but without much succes.
Any help with a piece of code in C will be greatly appreciated.Thanks
You could have a look at sscanf. Here's a bare-bones program. I am sure you can pick up from here:
#include <stdio.h>
int main(void)
{
char *hex = "0xF";
int i= 0;
sscanf(hex, "%x", &i);
printf("%d", i);
}
What you need is strtol function for integer types. You can use endptr to iterate through the string. For double you can use atof function, but you have to check firstly if the string contains a dot.
EDIT: As user3386109 mentioned strtod is a better solution for double.
Assuming that you have the string in an array:
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
int main()
{
char numbers_str[] = "1 -2 3.1 0xf 0xcc";
int ints[10];
double doubles[10];
int ints_idx = 0, doubles_idx = 0;
const char delims[] = " ";
char *it = strtok(numbers_str, delims);
while(it != NULL)
{
char *dot_it = strchr(it, '.');
// When a dot is found then a double number is found
if(dot_it)
doubles[doubles_idx++] = strtod(it, NULL);
// When a dot is not found then we've got an integer
else
ints[ints_idx++] = strtol(it, NULL, 0);
it = strtok(NULL, delims);
}
printf("Integers found: \n");
for(int i = 0; i < ints_idx; ++i)
printf("%d\n", ints[i]);
printf("Double numbers found: \n");
for(int i = 0; i < doubles_idx; ++i)
printf("%f\n", doubles[i]);
}
The easiest way to handle reading the values from the line is to work your way down the line with strtod. strtod takes two pointers as parameters. The first points to the beginning point to search for digits (or leading +/-) in order to convert the string representation of the number to a numeric value (all leading whitespace is skipped). The second pointer-to-pointer (endptr) will be set to the next character following the last character used in the conversion. You start your search for the next number to convert from there (e.g. set p = ep; and repeat the process).
You can consult the man page for further details, but to validate a successful conversion, you check that the pointer is not equal to the end-pointer (meaning digits were converted) and you check to make sure errno was not set. If there were no digits converted (meaning you had an invalid character), you simply want to scan forward in the line manually until your next +/- or 0-9 is found (or you hit the nul-terminating character).
You want to protect your array bounds and limit the number of values you try and store in your vector array by keeping a simple counter and exiting the loop when your array is full.
Here is a short example (NAN and INF checking omitted):
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#define VECSZ 5 /* if you need a constant, define one (or more) */
int main (int argc, char **argv) {
int i, n = 0;
double v[VECSZ] = {0.0};
char buf[BUFSIZ] = "";
FILE *fp = argc > 1 ? fopen (argv[1], "r") : stdin;
if (!fp) { /* validate file open for reading */
fprintf (stderr, "error: file open failed '%s'.\n", argv[1]);
return 1;
}
while (n < VECSZ && fgets (buf, BUFSIZ, fp)) { /* read each line */
char *p = buf, *ep; /* pointer, end-pointer for strtod */
while (n < VECSZ) { /* convert vales in buf until VECSZ */
double tmp = strtod (p, &ep);
if (p != ep) { /* digits converted */
if (!errno) /* validate no error */
v[n++] = tmp; /* add to vector */
p = ep; /* update pointer */
}
else { /* no digits converted */
fprintf (stderr, "error: no digits converted.\n");
/* scan forward to next valid '+,-,0-9' */
while (*p && *p != '-' && *p != '+' && (*p < '1' || '9' < *p))
p++;
if (*p) /* numbers remain in line */
continue;
break; /* otherwise get next line */
}
}
}
if (fp != stdin) fclose (fp); /* close file if not stdin */
for (i = 0; i < n; i++)
printf ("v[%d]=% g\n", i, v[i]);
return 0;
}
Example Input File
$ cat dat/strtod_vect.txt
1 -2 3.1 0xf 0xcc
Example Use/Output
$ ./bin/strtod_vect dat/strtod_vect.txt
v[0]= 1
v[1]=-2
v[2]= 3.1
v[3]= 15
v[4]= 204
Look things over and let me know if you have further questions. You can check strtod(3) - Linux man page for further details and error checking that can be done.

Resources