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
Related
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
$
I'm trying to create a program that receives a word and two chars to be replaced inside that word using pointers, but I cannot figure it out how.
my .h file:
typedef struct pointer{
char message[100];
char start;
char end;
} tPointer;
tPointer readMsg();
my .c file:
tPointer readMsg(){
tPointer nPointer;
printf("Input word, char start and char end (between spaces): ");
scanf("%s %c %c", nPointer.message, &nPointer.start, &nPointer.end);
return(nPointer);
}
my main.c file:
int main(){
tPointer pointer;
pointer = readMsg();
printf("%s, %c, %c\n", pointer.message, pointer.start, pointer.end);
for (int i = 0; pointer.message[i] != '\0'; ++i) {
if (pointer.start == pointer.message[i])
{
printf("tem: %c\n", pointer.message[i]);
}
}
return 0;
}
The start input is the character inside message that I want to change, the end input is the character I will trade with start to create the new message
I have to swap the characters inside that for loop, right?
Example1:
message = stack
start = a
end = b
new message = stbck
Example2:
message = overflow
start = o
end = z
new message = zverflzw
From your updated examples, it looks like you want to find the first occurrence of start and replace that character with end. Much of the confusion with your question stems from the fact that start and end have nothing to do with what it is you are attempting to do. Use descriptive names to help keep your logic straight, e.g.
#include <stdio.h>
#define MAXC 1024 /* if you need a constant, #define one (or more) */
typedef struct {
char message[MAXC]; /* don't use magic-numbers, use a constant */
char find; /* use descriptive names */
char replace;
} tPointer;
(note: the struct-tag pointer was unused and omitted)
With your tPointer readMsg() function, you have no way to tell if the input succeeded or if the string and two characters requested were actually entered. It is critical that you validate EVERY input and validate the input matches what was requested.
Rather than returning type tPointer (which if uninitialized provides no way to tell what was entered), pass a tPointer pointer as a parameter and return type int with 0 on failure and 1 on success (or vice-versa -- up to you). That way you can check the return in the caller and know whether readMsg() succeeded or failed, e.g.
/* choose return type that can indicate success/failure
* of input operation. Returns 1 on success, 0 otherwise
*/
int readMsg (tPointer *tp)
{
char buf[MAXC]; /* buffer to hold line of input */
/* read input into buf */
fputs ("Input word, find char, replace char (between spaces): ", stdout);
if (!fgets (buf, MAXC, stdin)) { /* always validate EVERY input */
puts ("(user canceled input)");
return 0;
}
/* separate using sscanf() -- always validate correct input */
if (sscanf (buf, "%s %c %c", tp->message, &tp->find, &tp->replace) != 3) {
fputs ("error: invalid input.\n", stderr);
return 0;
}
return 1; /* return success */
}
(note: the user generating a manual EOF to cancel input by pressing Ctrl + d (or Ctrl + z on windows) is valid input by the user that should be handled)
Also note, that a larger buffer was used to read the entire line into buf and then separate the string and characters in buf using sscanf(). This insures that if any EXTRA characters are entered by the user they are read and discarded. Failure to do so would leave any extra characters in stdin (unread) just waiting to bite you on your next attempted read (such as if you were doing the read in a loop to let the user enter more than one set of data)
Since you wrote a function for readMsg(), you may as well write another for replaceChar(). The return type can be int again and you can pass your tPointer pointer as a parameter, returning 1 on success or 0 if the find character was not found in message, e.g.
/* replace 'find' char with 'replace' char.
* returns 1 on success, 0 if find char not found.
*/
int replaceChar (tPointer *tp)
{
for (int i = 0; tp->message[i]; i++) { /* loop over each char */
if (tp->message[i] == tp->find) { /* check if find char */
tp->message[i] = tp->replace; /* replace it */
return 1; /* return success */
}
}
return 0; /* return failure - char not found */
}
Now in main() check the return of each function to ensure both succeed before outputting the results. On failure, handle the error. You can do something similar to:
int main (void) {
tPointer ptr = { .message = "" }; /* initialize struct all zero */
if (!readMsg (&ptr)) { /* validate read */
return 1; /* handle failure */
}
if (!replaceChar (&ptr)) { /* validate replacement */
fprintf (stderr, "error: char not found '%c'.\n", ptr.find);
return 1; /* handle failure */
}
/* output results */
printf ("find = %c\nreplace = %c\nupdated string: %s\n",
ptr.find, ptr.replace, ptr.message);
}
(note: you can paste the 4-pieces of code together for the complete program)
Example Use/Output
A valid replacement:
$ ./bin/tpointer-find-replace
Input word, find char, replace char (between spaces): Overflow o E
find = o
replace = E
updated string: OverflEw
Invalid input:
$ ./bin/tpointer-find-replace
Input word, find char, replace char (between spaces): Overflow o
error: invalid input.
Character Not Found:
$ ./bin/tpointer-find-replace
Input word, find char, replace char (between spaces): Overflow s y
error: char not found 's'.
User cancels input by generating manual EOF:
$ ./bin/tpointer-find-replace
Input word, find char, replace char (between spaces): (user canceled input)
Let me know if you have further questions.
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.
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.
I face a strange issue while trying to get user input through my code. I am pretty sure that the issue is not with the code but related to OS like standard input stream (stdin) or something like that, but since I don't have another machine with similar OS setup (as it's practically impossible to find an SCO machine nowadays), I expect some programmatic workarounds to solve this. My program reads a stream of alphanumeric characters from the user terminated by a '\n'.
But no matter how I try this to achieve this by different means, it just accepts the initial 256 characters. Initially I suspected the issue is with the fgets function , but when I use try to read the same value from a file using fgets, its working as expected.
Approach 1:
main()
{
char szInLine[999];
memset(szInLine, 0, sizeof(szInLine));
fprintf(stdout, "\nPlease enter the encrypted value:\n");
if (fgets(szInLine, 997, stdin) == NULL)
return(1);
fprintf(stdout, "Encrypted data string contains %i characters: %s\n",
strlen(szInLine), szInLine);
}
Approach 2:
while(ch = getc(stdin)) != EOF)
{
if((*szInLine++ = ch) == '\n')
{
break;
}
}
*szInLine = '\0';
fprintf(stdout, "Encrypted data string contains %i characters: %s\n", strlen(szInLine), szInLine);
Output for both cases : "Encrypted data string contains 256 characters: abcde.....
Other approaches I already tried but didn't succeed include changing the data type of the buffer which holds the value (from string to unsigned long), dynamically allocating memory to the buffer, setting stdin as unbuffered e.t.c.
OS environment :
SCO Unix, 32bit
Compiler:
CC
See the ioctl() and stty() manual page on the SCO web site. You should be able to retrieve the difference in the settings by testing terminal vs. redirection.
well, your programs (both) have errors:
/* you should include <stdio.h> so fgets() can return a char *,
* If you don't, it's assumed fgets() returns an int value. */
#include <stdio.h>
main()
{
char szInLine[999];
memset(szInLine, 0, sizeof(szInLine)); /* you don't need this */
fprintf(stdout, "\nPlease enter the encrypted value:\n");
/* fgets accepts a buffer and its size, it will reserve space for
* one '\0' char. */
if (fgets(szInLine, sizeof szInLine, stdin) == NULL) {
/* it is good to print some diagnostic if you receive EOF */
return(1);
}
fprintf(stdout, "Encrypted data string contains %i characters: %s\n",
strlen(szInLine), szInLine);
/* you should return 0, here */
return(0);
}
The second is even worse:
/* unbalanced parenthesis, you lack a parenthesis after 'while' keyword */
while(ch = getc(stdin)) != EOF)
{
if((*szInLine++ = ch) == '\n')
{
break;
}
}
*szInLine = '\0';
/* if you move the pointer 'szInLine' it will always be pointing to the end of
* the string, so this printf will show 0 characters and an empty string, you
* had better to save the pointer at the beginning, so you don't lose the
* reference to the string beginning.
*/
fprintf(stdout, "Encrypted data string contains %i characters: %s\n", strlen(szInLine), szInLine);
This should work:
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
int main()
{
char buffer_in[1000];
char buffer_out[1000];
while (fgets(buffer_in, sizeof buffer, stdin)) {
/* you'll get a line of up to 'sizeof buffer_in - 1' chars with an
* ending '\n' (or a truncated if the line has more than 'sizeof
* buffer_in - 1' chars. Also, you'll have a '\n' at the end of the
* buffer, if the line filled partially the buffer. */
fprintf(stderr,
"String read (%d chars): %s", /* this is why I don't put a '\n' here */
strlen(buffer_in),
buffer_in);
/* encrypt(buffer_in, sizeof buffer_in, buffer_out, sizeof buffer_out); */
}
/* here you got EOF */
return 0;
}
or if you want to use getc():
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
int main()
{
/* it is very important that c be an int, see manual
* page of fgetc(), getch() or getchar() */
int c;
char buffer[1000], *p = buffer;
/* we check for buffer length and for EOF. As we are doing the hard
* work ourselves, we have to check for 'sizeof buffer - 1' to allow
* space for the '\0'. */
while ((p < buffer + sizeof buffer - 1) && ((c = getchar()) != EOF)) {
if (c == '\n') { /* A NEWLINE, act on buffer, and skip it. */
*p = '\0'; /* end the string */
printf("Read %d chars: %s\n", p - buffer, buffer);
/* crypt it ... */
/* ... */
p = buffer; /* reset buffer */
continue;
}
*p++ = c; /* add the character to the buffer */
}
/* here you got EOF */
return 0;
}
One final note:
Don't post snippets of code, but complete examples, as it is very difficult to identify which errors are mistakes on copying the code here, or which are mistakes you have made in the original program.