Convert hexadecimal numbers to decimal numbers - c

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.

Related

list convertion in 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
$

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.

how to get specific rows from csv file with exact data using c?

This is my csv file, i want to get only those row which start with character "A" so i got my output but with some addition column as '0' please help me to find were i went wrong?
And one more thing i want to remove specific column like bread,anName,ot
Name,id,bread,anName,Ot,number
A,1,animal,tiger,op,8.1
M,2,animal,toper,ip,9.1
A1,7,animal,dog,cp,Na11
A2,9,animal,mouse,ap,0
A23,9,animal,pouch,gp,Na11
#include <stdio.h>
#include <stdlib.h>
#define NUMLETTERS 100
typedef struct {
char Name[100];
int id;
char number[100];
} record_t;
int main(void) {
FILE *fp;
record_t records[NUMLETTERS];
int count = 0, i;
fp = fopen("letter.csv", "r");
if (fp == NULL) {
fprintf(stderr, "Error reading file\n");
return 1;
}
while (fscanf(fp, "%s,%d,%s", records[count].name, &records[count].id, records[count].number) == 1)
count++;
for (i = 0; i < count; i++) {
if(records[i].Name[0] == 'A'){
printf("%s,%d,%s\n", records[i].Name, records[i].id, records[i].number);
}
}
fclose(fp);
return 0;
}
i want output as:
A,1,8.1
A1,7,Na11
A2,9,0
A23,9,Na11
You have two problems:
The %s format specifier tells fscanf to read a space-delimited string. Since the the records aren't space-delimited the first %s will read the whole line.
The fscanf function returns the number of successfully parsed elements it handled. Since you attempt to read three values you should compare with 3 instead of 1.
Now for one way how to solve the first problem: Use the %[ format specifier. It can handle simple patterns and, most importantly, negative patterns (read while input does not match).
So you could tell fscanf to read a string until it finds a comma by using %[^,]:
fscanf(fp, " %[^,],%d,%s", records[count].Refdes, &records[count].pin, records[count].NetName)
The use of the %[ specifier is only needed for the first string, as the second will be space-delimited (the newline).
Also note that there's a space before the %[ format, to read and ignore leading white-space, like for example the newline from the previous line.
i want to get only those row which start with character "A"
i want to remove the number which coming between A and tiger,
If I understand you correctly and you only want to store rows beginning with 'A', then I would adjust your approach to read each line with fgets() and then check whether the first character in the buffer is 'A', if so, continue; and get the next line. The for those lines that do start with 'A', simply use sscanf to parse the data into your array of struct records.
For your second part of removing the number between 'A' and "tiger", there is a difference between what you store and what you output (this comes into play in storing only records beginning with 'A' as well), but for those structs stored where the line starts with 'A', you can simply not-output the pin struct member to get the output you want.
The approach to reading a line at a time will simply require that you declare an additional character array (buffer), called buf below, to read each line into with fgets(), e.g.
char buf[3 * NUMLETTERS] = "";
...
/* read each line into buf until a max of NUMLETTERS struct filled */
while (count < NUMLETTERS && fgets (buf, sizeof buf, fp)) {
record_t tmp = { .Refdes = "" }; /* temporary struct to read into */
if (*buf != 'A') /* if doesn't start with A get next */
continue;
/* separate lines beginning with 'A' into struct members */
if (sscanf (buf, " %99[^,],%d,%99[^\n]",
tmp.Refdes, &tmp.pin, tmp.NetName) == 3)
records[count++] = tmp; /* assign tmp, increment count */
else
fprintf (stderr, "%d A record - invalid format.\n", count + 1);
}
A short example putting that to use and (since we are not sure what "remove" is intended to be), we have included a pre-processor conditional that will only output the .Refdes and .NetName members by default, but if you either #define WITHPIN or include the define in your compile string (e.g. -DWITHPIN) it will output the .pin member as well.
#include <stdio.h>
#include <stdlib.h>
#define NUMLETTERS 100
typedef struct {
char Refdes[NUMLETTERS];
int pin;
char NetName[NUMLETTERS];
} record_t;
int main (int argc, char **argv) {
record_t records[NUMLETTERS];
char buf[3 * NUMLETTERS] = "";
int count = 0, i;
/* 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;
}
/* read each line into buf until a max of NUMLETTERS struct filled */
while (count < NUMLETTERS && fgets (buf, sizeof buf, fp)) {
record_t tmp = { .Refdes = "" }; /* temporary struct to read into */
if (*buf != 'A') /* if doesn't start with A get next */
continue;
/* separate lines beginning with 'A' into struct members */
if (sscanf (buf, " %99[^,],%d,%99[^\n]",
tmp.Refdes, &tmp.pin, tmp.NetName) == 3)
records[count++] = tmp; /* assign tmp, increment count */
else
fprintf (stderr, "%d A record - invalid format.\n", count + 1);
}
if (fp != stdin) /* close file if not stdin */
fclose (fp);
for (i = 0; i < count; i++)
#ifdef WITHPIN
printf ("%-8s %2d %s\n",
records[i].Refdes, records[i].pin, records[i].NetName);
#else
printf ("%-8s %s\n", records[i].Refdes, records[i].NetName);
#endif
}
Example Use/Output
$ ./bin/getaonly dat/getasonly.txt
A tiger
A1 dog
A2 mouse
A23 pouch
If you define -DWITHPIN in your compile string, then you will get all three outputs:
$ ./bin/getaonly dat/getasonly.txt
A 1 tiger
A1 7 dog
A2 9 mouse
A23 9 pouch
(note: with the data stored in your array, you can adjust the output format to anything you need)
Since there is some uncertainty whether you want to store all and output only records beginning with 'A' or only want to store records beginning with 'A' -- let me know if I need to make changes and I'm happy to help further.

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

File Handling + character manipulation

this is my code.
the input numbers are
1234567890
the output of this code should be
(123)456-7890
but the output is different. Any advice or error fixes in my code?
#include <stdio.h>
#include <ctype.h>
int main()
{
char ch;
int a[100], s[100], str, k, i;
FILE *fp;
fp = fopen("number.c", "r");
while ( ( ch = fgetc(fp) ) != EOF )
{
k = 0;
a[k] = '(';
a[k+4] = ')';
a[k+8] = '-';
for (i = 0; s[i] != '\0'; i++)
{
if (isdigit(s[i]))
{
a[k++] = s[i];
if (k == 3)
{
k++;
}
}
printf("%s", a);
}
fclose(fp);
return 0;
}
}
This looks like an assignment from a first year course in CS. If so, I would say find a TA during office hours and discuss.
There are several issues with the code:
Your outer loop is intending to read a line at a time from a file and populate the s array. It is instead reading a character at a time and populating the ch variable.
As mentioned in the comments, you are not accounting for the "-" when putting characters into the a array.
You are not terminating your string in the a array.
There may be different schools of thought on this in c, but I would make s and a char[] instead of int[].
My advice would be to get out a piece of paper and make spaces for each of your variables. Then read your code line by line and manipulate your variables the way you expect the computer to execute what is written. If you can read what is written, rather than what you expect the code to do, then the issues will become apparent.
/* ugly: The old phone #
nice: The formatted phone #
*/
#include <stdio.h>
void fmtpn(const char *ugly, char *nice)
{
int i, j;
/* add one to allocate space for null terminator */
char first3[3 + 1], next3[3 + 1], last4[4 + 1];
if (strlen(ugly) != 10 || containsalpha(ugly)) {
strcpy(nice, "Invalid pn!");
return;
}
for (i = 0; i < 3; ++i)
first3[i] = ugly[i];
first3[i] = 0; /* null terminate the string */
for (j = 0; j < 3; ++i, ++j)
next3[j] = ugly[i];
next3[j] = 0; /* null terminate */
for (j = 0; j < 4; ++i, ++j)
last4[j] = ugly[i];
last4[j] = 0; /* null terminate */
sprintf(nice, "(%s) %s-%s", first3, next3, last4);
}
To read from the file:
FILE *fp;
char ugly[32], good[32];
if (fp = fopen("file", "r")) {
fgets(ugly, 32, fp);
fmtpn(ugly, good);
puts(good);
}
No love for sscanf?
#include <stdio.h>
int prettyprint(char *input, char *output)
{
int n[10], ret;
ret = sscanf(input, "%1d%1d%1d%1d%1d%1d%1d%1d%1d%1d", &(n[0]), &(n[1]),
&(n[2]), &(n[3]), &(n[4]), &(n[5]), &(n[6]),
&(n[7]), &(n[8]), &(n[9]));
if (ret != 10)
fprintf(stderr, "invalid input\n");
sprintf(output, "(%1d%1d%1d) %1d%1d%1d-%1d%1d%1d%1d",
n[0], n[1], n[2],
n[3],n[4], n[5],
n[6], n[7], n[8], n[9]);
return 0;
}
int main(int argc, char **argv)
{
char digits[] = "0123456789";
char output[256];
prettyprint(digits, output);
printf("%s\n", output);
}
You have other options aside from looping through your sting to build the phone number. Sometimes, when dealing with fixed strings or known quantities, a straight forward packing of the characters into a fixed format is a lot simpler than picking the characters out of loops.
For example, here you know you are dealing with a 10 char string of digits. In your code you can read/parse each line into a string of 10 digits. Then your only task is to format those 10 digits into the phone number. Using a pointer for each string and then strncpy is about as easy as anything else:
#include <stdio.h>
#include <string.h>
int main (void) {
char *digits = "1234567890";
char *p = digits;
char phonenum[15] = {0};
char *pf = phonenum;
/* build formatted phone number */
*pf++ = '(';
strncpy (pf, p, 3);
pf += 3, p += 3;
*pf++ = ')';
*pf++ = ' '; /* note: included space, remove line if unwanted */
strncpy (pf, p, 3);
pf += 3, p += 3;
*pf++ = '-';
strncpy (pf, p, 4);
pf += 4;
*pf = 0;
printf ("\n digits : %s\n phone : %s\n\n", digits, phonenum);
return 0;
}
Output
$ ./bin/phnumbld
digits : 1234567890
phone : (123) 456-7890
You can easily turn the code above into a simple function that creates a formatted phone number given any 10-digit string. Breaking your code down into functional pieces not only makes your code easier to read and write, but it also builds flexibility and ease of maintenance into your code. Here were you dealing with an actual dial-string that included the international dialing prefix and country code, you could easily format the last 10 digits of the longer string by using a pointer to the appropriate beginning character.
With File Handling
Writing anything in C is no different. You simply break the problem down into discrete operations and then write small bits of code to handle each part of the problem. As you get more experience, you will build a collection of routines to handle most situations.
Below the code declare three constants. ACPN (area code phone number length), MAXC (maximum digits in dial string including country code and international dialing prefix), and MAXS (maximum number of chars in line to read from file)
You options for reading lines of data in C are broken into two broad categories, character oriented input and line oriented input. When reading lines from a file, in most cases line oriented input is the proper choice. You read a line of data at a time into a buffer, then you parse the information you need from the buffer. Your primary choices for line oriented input in C are fgets and getline. We use the standard fgets below.
Below, the code will read a line of data, then call get_n_digits to extract up to MAXC digits in the line into a separate buffer holding the digits (numstr). The number string is then passed to fmt_phone which takes the last 10 digits in the string (discarding any initial country-code or int'l dialing prefix) and formatting those digits into a telephone number format. You can adjust any part as needed to meet your input file:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define ACPN 10
#define MAXC 16
#define MAXS 256
size_t strip_newline (char *s);
char *get_n_digits (char *numstr, char *s, size_t n);
char *fmt_phone (char *fmts, char *s, size_t n);
int main (int argc, char **argv) {
/* open file or read from stdin */
FILE *fp = argc > 1 ? fopen (argv[1], "r") : stdin;
if (!fp) {
fprintf (stderr, "error: failed to open file for reading\n");
return 1;
}
char numstr[MAXC] = {0}; /* string of digits (max MAXC - 1) */
char fmtstr[MAXC] = {0}; /* formatted phone number string */
char line[MAXS] = {0}; /* line buffer holding full line */
/* read each line from fp (up to MAXS chars) */
while (fgets (line, MAXS, fp))
{
size_t len = strip_newline (line); /* strip trailing newline */
get_n_digits (numstr, line, MAXC); /* get MAXC digits from line */
printf ("\n read : %s (%zu chars), taking last 10 of : %s\n",
line, len, numstr);
/* format last 10 digits into phone number */
fmt_phone (fmtstr, numstr, ACPN);
printf (" phone : %s\n", fmtstr);
}
if (fp != stdin) fclose (fp);
return 0;
}
size_t strip_newline (char *s)
{
size_t len = strlen (s);
s [--len] = 0;
return len;
}
/* extract upto n digits from string s, copy to numstr */
char *get_n_digits (char *numstr, char *s, size_t n)
{
char *p = s;
size_t idx = 0;
while (*p && idx < n - 1) {
if (*p >= '0' && *p <= '9')
numstr[idx++] = *p;
p++;
}
numstr[idx] = 0;
return numstr;
}
/* format last n (10) digits in s into a formatted
telephone number: (xxx) yyy-zzzz, copy to fmts.
'last 10' accounts for country code and international
dialing prefix at beginning of dial string.
*/
char *fmt_phone (char *fmts, char *s, size_t n)
{
/* validate strings */
if (!fmts || !s) {
fprintf (stderr, "%s() error: invalid string parameter.\n", __func__);
*fmts = 0;
return fmts;
}
/* validate length of n */
if (n < ACPN) {
fprintf (stderr, "%s() error: insufficient size 'n' for format.\n", __func__);
*fmts = 0;
return fmts;
}
/* validate length of s */
size_t len = strlen (s);
if (len < n) {
fprintf (stderr, "%s() error: insufficient digits in string.\n", __func__);
*fmts = 0;
return fmts;
}
/* set start pointer to last 10 digits */
char *p = len > n ? s + len - n : s;
char *pf = fmts;
/* build formatted phone number */
*pf++ = '(';
strncpy (pf, p, 3);
pf += 3, p += 3;
*pf++ = ')';
*pf++ = ' ';
strncpy (pf, p, 3);
pf += 3, p += 3;
*pf++ = '-';
strncpy (pf, p, 4);
pf += 4;
*pf = 0;
return fmts;
}
Compile with gcc -Wall -Wextra -o progname sourcename.c
Example Input
$ cat dat/pnumtest.txt
123456789012345
12345678901234
1234567890123
123456789012
12345678901
1234567890
123456789
Example Output
$ ./bin/phnum dat/pnumtest.txt
read : 123456789012345 (15 chars), taking last 10 of : 123456789012345
phone : (678) 901-2345
read : 12345678901234 (14 chars), taking last 10 of : 12345678901234
phone : (567) 890-1234
read : 1234567890123 (13 chars), taking last 10 of : 1234567890123
phone : (456) 789-0123
read : 123456789012 (12 chars), taking last 10 of : 123456789012
phone : (345) 678-9012
read : 12345678901 (11 chars), taking last 10 of : 12345678901
phone : (234) 567-8901
read : 1234567890 (10 chars), taking last 10 of : 1234567890
phone : (123) 456-7890
read : 123456789 (9 chars), taking last 10 of : 123456789
fmt_phone() error: insufficient digits in string.
phone :
Note: there are many, many different ways to approach this problem, this is but one.
Note2: while not required for this code, I included a function showing how to strip the trailing newline ('\n') from the input read by fgets. It is never a good idea to leave newlines dangling from strings in your code. While here they would not have caused a problem, in most cases they will bite you if your are not aware of them. So get in the practice of handling/removing the trailing newlines when using fgets or getline to read from a file. (note: getline provides the number of characters actually read as its return, so you can avoid calling strlen and simply use the return of getline to remove the newline in that case.)

Resources