I've been working on this project that deals with an input file that looks like the following:
A 0.0345
B 0.3945
...
Z 0.2055
Basically, I'm reading each line, after reading the line, I want to pull only the double out of the string. I'm trying to use strtod() and everyone's examples seem to use it perfectly fine but in my example, it only returns 0.
Here is my code:
for (count = 0; count < 26; count++)
{
char buffer[100]; /* Buffer for the line read by fgets */
char *letters; /* Stores the string calculated by strtod */
double relFreq = 0.0;
if (fgets(buffer, 100, stream) != NULL)
{
relFreq = (double)strtod(buffer, &letters); /* Since the format is A 0.0000, we're just grabbing the double */
*(sampleOne + count) = relFreq; /* Add the relFreq to the array */
}
}
Through the use of a debugger, buffer has the correct data, for instance A 0.0345\n but when strtod() returns a value it's always 0. Any help would be appreciated!
Since the first character is non-numeric, strtod should be returning letters equal to buffer (the first character is invalid), indicating that nothing has been read.
You should be able to fix this by passing &buffer[2], where the number starts:
relFreq = strtod(&buffer[2], &letters);
Demo.
I'm not sure why you think that strtod will ignore the leading letter on your lines, but it will not. You'll need to do something like this:
relFreq = (double) strtod(&buffer[1], &letters);
And since you are not using letters:
relFreq = (double) strtod(&buffer[1], NULL);
You could use sscanf. For example
#include <stdio.h>
int main(void)
{
char *s = "A 0.0345\n";
char c;
double d;
sscanf( s, "%c %lf", &c, &d );
printf( "%f\n", d );
return 0;
}
The output os
0.034500
As for your code then you could at first to find a digit in the buffer and apply function strtod to the pointer that points to the found digit provided that the first field in the buffer is always a non-digit.
Related
I have a string that looks like this: {opt,2}{home,4}... I have to scan opt and 2 into a string and integer. I am doing this:
char str[40] = "{opt,2}{home,4}";
char s[20];
int *x;
sscanf(str, "{%[^,],%[0-9]}", s,&x);
This segfaults. What is the right way?
To scan text like that, you don't need regex, you could just use simple scanf specifiers.
char s[16]; /* The string in the pair */
int n; /* The integer in the pair */
int len; /* The length of the pair */
for (; sscanf(buf, "{%[^,],%d}%n", s, &n, &len) == 2; buf += len) {
/* use str and n */
}
The %n specifier simply receives the number of characters read in the sscanf call, and stores it in the matching argument (in this case, len). We use this value so that we can increment the buf that is being read from, so that the next string-int pair may be read.
I'm trying to make a function to validate mobile entry, the mobile number MUST starts with 0 and is 11 numbers (01281220427 for example.)
I want to make sure that the program gets the right entry.
This is my attempt:
#include <stdio.h>
#include <strings.h>
void integerValidation(char x[15]);
int main(int argc, char **argv)
{
char mobile[15];
integerValidation(mobile);
printf("%s\n\n\n", mobile);
return 0;
}
void integerValidation(char x[15]){
char input[15];
long int num = -1;
char *cp, ch;
int n;
printf("Please enter a valid mobile number:");
while(num<0){
cp = fgets(input, sizeof(input), stdin);
if (cp == input) {
n = sscanf(input, "%ld %c", &num, &ch);
if (n!=1) {printf("ERROR! Please enter a valid mobile number:");
num = -1;
}
else if (num<0)
printf("ERROR! Please enter a valid mobile number:");
else if ((strlen(input)-1)>11 || (strlen(input)-1)<11 || strncmp(&input[0], "0", 1) != 0){
printf("ERROR! Please enter a valid mobile number:");
num = -1;
}
}
}
long int i;
i = strlen(input);
//Because when I try to print it out it prints a line after number.
strcpy(&input[i-1], "");
strcpy(x, input);
}
Now, if I don't use
strcpy(&input[i-1], "");
the array prints a new line after the number, what would be a good fix other than mine? and how can I make this function optimized and shorter?
Thanks in advance!
Edit:
My question is: 1. Why does the input array prints a new line in the end?
2. How can I make this code shorter?
End of edit.
If you insist on using sscanf(), you should change the format this way:
int integerValidation(char x[15]) {
char input[15], c;
printf("Please enter a valid mobile number:");
while (fgets(input, sizeof(input), stdin)) {
if (sscanf(input, "%11[0123456789]%c", x, &c) == 2
&& x[0] == '0' && strlen(x) == 11 && c == '\n') {
// number stored in `x` is correct
return 1;
}
printf("ERROR! Please enter a valid mobile number:");
}
x[0] = '\0'; // no number was input, end of file reached
return 0;
}
%12[0123456789] parses at most 11 characters that must be digits.
%c reads the following character, which should be the trailing '\n'.
I verify that both formats have been matched, and the number starts with 0 (x[0] == '0') and it has exactly 11 digits.
You're seeing the newline, since fgets() reads until an EOF or a newline is received. The newline is stored in the buffer, and after that the string is terminated with '\0'.
An alternative would be to directly overwrite the newline with another null-byte: input[i-1] = '\0' (which basically does the same thing as your solution, but saves a function call).
The same goes for the check with strncmp with length 1, you can directly check input[0] == '0'. Note that you have to compare against '0' (char) here, not "0" (string).
A few other things I'm seeing:
You can also spare the %c in the format string for sscanf (you're never evaluating it anyway, since you're checking for 1 as return value), which also eliminates the need for char ch.
Also, you're passing char x[15] as argument to your function. This is a bit misleading, because what actually gets passed is a pointer to a char array (try using sizeof(x), your compiler will most likely issue a warning about the size of char * being returned by sizeof()).
What you could do is to ditch the char array input, which you're using as temporary buffer, and use the buffer which was handed over as argument. For this to be save, you should use a second funcion parameter to specify the size of the buffer which was handed to the function, which would result in a function header like as follows:
void integerValidation(char *input, size_t len);
With this, you'd have to use len instead of sizeof(input). The following question provides more detail why: C: differences between char pointer and array
Since you're not using a temporary buffer anymore, you can remove the final call to strcpy().
There are also a lot of checks for the number length/format. You can save a few:
If you use %lu instead of %ld no signed numbers are being converted, which saves you the check for num < 0.
You're checking whether the length of the read number is <11 or >11 - why not just check for !=11?
You're calling strlen() three times on the input-buffer (or still twice with the reworked check for lengh 11) - it makes sense to call it once, save the length in a variable and use that variable from then on, since you're not altering the string between the calls.
There is already an accepted answer, but for what it's worth, here is another.
I made several changes to your code, firstly avoiding "magic numbers" by defining the phone number length and an arbitrarily greater string length. Then there is no point passing an array x[15] to a function since it pays no regard to its length, might as well use the simpler *x pointer. Next, I return all reasons for failure back to the caller, that's simpler. And instead of trying to treat the phone number as a numeric entry (note: letters, spaces, hyphens, commas and # can sometimes be a part of phone number too) I stick to a character string. Another reason is that the required leading zero will vanish if you convert the entry to an int of some size. I remove the trailing newline that fgets() reads with the input line, and the result is this.
#include <stdio.h>
#include <string.h>
#include <ctype.h>
#define MAXLEN 11
#define STRLEN (MAXLEN+10)
int integerValidation(char *x);
int main(int argc, char **argv)
{
char mobile[STRLEN];
while (!integerValidation(mobile)) // keep trying
printf("Invalid phone number\n");
printf("%s\n\n\n", mobile); // result
return 0;
}
int integerValidation(char *x)
{
int i, len;
printf("Please enter a valid mobile number:");
if(fgets(x, STRLEN, stdin) == NULL) // check bad entry
return 0;
x [ strcspn(x, "\r\n") ] = 0; // remove trailing newline etc
if((len = strlen(x)) != MAXLEN) // check length
return 0;
if(x[0] != '0') // check leading 0
return 0;
for(i=1; i<len; i++) // check all other chars are numbers
if(!isdigit(x[i]))
return 0;
return 1; // success
}
Is it possible to increment a number alone within a string?
So let's say I have:
char someString = "A0001";
Is there a way to increment the number '0001'? To make it A0002, A0003 etc?
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
char *strinc(const char *str, int d, int min_width){
char wk[12];//12:max length of sizeof(int)=4
char *p;
int len, d_len, c;
c = len = strlen(str);
while(isdigit(str[--c]));
++c;
d += strtol(&str[c], NULL, 10);
if(d<0) d = 0;
d_len = sprintf(wk, "%0*d", min_width, d);
p = malloc((c+d_len+1)*sizeof(char));
strncpy(p, str, c);
p[c]='\0';
return strcat(p, wk);
}
int main(void){
char *someString = "A0001";
char *label_x2, *label_x3;
label_x2 = strinc(someString, +1, 4);
printf("%s\n", label_x2);//A0002
label_x3 = strinc(label_x2, +1, 4);
printf("%s\n", label_x3);//A0003
free(label_x2);
label_x2 = strinc("A0008", +5, 4);
printf("%s\n", label_x2);//A0013
free(label_x3);
label_x3 = strinc(label_x2, -8, 4);
printf("%s\n", label_x3);//A0005
free(label_x2);
free(label_x3);
return 0;
}
no u cannot do it because it is a constant
The simple answer is that there is no "easy" way to do what you're asking. You would have to parse the string, extract the numerical portion and parse into a number. Increment the number and then print that number back into your string.
You could try the following simple example to base something on...
EDIT: Just read BLUEPIXY's answer... he presents a nice function that will do it for you, return you a new string, which doesn't have the width restriction of my simple answer...
There are some points worth noting...
Use char someString[] = "A0001"; and not char *someString = "A0001";. The reason is that the former allocates memory on the stack for the string, the latter is a pointer to a string in memory. The memory location decided upon by the compiler in the latter case and is not always guaranteed to be writable.
Crappy #define for snprintf on Windows... not sure that's a good thing. The point is really use a safe buffer writing function that won't overflow the bounds of your array.
The snprintf format string "%0*u" Formats an unsigned integer with a minimum width specified by the argument to the left of the actual integer and the zero tells it to left pad with zeros if necessary.
If your number increases to a width greater than, in this case, 4 digits, the buffer won't overflow, but your answers will look wrong (I haven't put in any logic to increment the buffer size)
I am assuming the the format of the string is always a set of non-numerical-digits, followed by a set of numerical digits and then a null terminator.
Now the code...
#include <stdio.h>
#include <stdlib.h>
#include <ctype.h>
#include <string.h>
#ifdef WIN32
#define snprintf sprintf_s
#endif
int main(int argc, char* argv[])
{
/* Assume that the string format is letters followed by numbers */
/* Note use someString[] and NOT someString* */
char someString[] = "A0001";
char *start = someString;
char *end = start + strlen(someString); /* End points to the NULL terminator */
char *endOfParse;
char c;
unsigned long num;
ptrdiff_t numDigits;
/* Find first numeric value (start will point to first numeric
* value or NULL if none found */
while( true )
{
c = *start;
if( c == '\0' || isdigit(c) )
break;
++start;
}
if( c == '\0' )
{
printf("Error: didn't find any numerical characters\n");
exit(EXIT_FAILURE);
}
/* Parse the number pointed to by "start" */
num = strtoul(start, &endOfParse, 0);
if(endOfParse < end )
{
printf("Error: Failed to parse the numerical portion of the string\n");
exit(EXIT_FAILURE);
}
/* Figure out how many digits we parsed, so that we can be sure
* not to overflow the buffer when writing in the new number */
numDigits = end - start;
num = num + 1;
snprintf(start, numDigits+1, "%0*u", numDigits, num); /* numDigits+1 for buffer size to include the null terminator */
printf("Result is %s\n", someString);
return EXIT_SUCCESS;
}
You can't do it simply because its not as simple to machine as it looks to you. There are a lot of things you need to understand about what you are trying to do first. For example, What part of string are you taking as a number which is to be incremented?
Last digit only?
A number which will be followed by SINGLE alphabet?
A number which may be followed by any number of alphabets?
LAST number in a string, for example A33B43 would mean to increment 33 or 43?
When you have answers to all such questions, you can implement them in a function. One of the many possible approaches thereafter can be to make a new substring which will represent the number to be incremented(this substring is to be taken out from your someString). Then use atoi() to convert that string into number, increment the number and replace this incremented number as a string in someString.(someString needs to be String or char * btw).
I tried using atoi but I can only get to the 500 that way. Not sure where to go from here.
You can use strtol to "tokenize" a chain of whitespace-separated integers:
int a, b;
char src[] = "500 600";
char *tmp = src;
// The first call to strtol parses 500
a = strtol(tmp, &tmp, 10);
// The call looks the same, but tmp now points at the space between 500 and 600
// The next call to strtol skips the space, and parses 600
b = strtol(tmp, &tmp, 10);
Obviously, this code is skeletal, and skips the relevant checks. See documentation for the precise information on how strtol handles various unexpected situations.
Read the entire string using fgets.
Tokenize the string based on the space delimeter using strtok.
Convert the token to the integer.
If string has more characters, go to step 2.
Using sscanf will be simple.
An example to use sscanf is as below:
char str[] = "500 600";
int a, b;
if(sscanf(str, "%d%d", &a, &b)==2) {
// OK
} else {
// failed to parse
}
If you do not need to test the sscanf has failed or not, write as below:
sscanf(str, "%d%d", &a, &b);
char &s = "500 600";
int x, y;
if (2 == sscanf(s, "%d %d", &x, &y))
{
/* Everything is ok */
}
else
{
/* Have another go! */
}
should do the trick
I have some lines I want to parse from a text file. Some lines start with x and continue with several y:z and others are composed completely of several y:zs, where x,y,z are numbers. I tried following code, but it does not work. The first line also reads in the y in y:z.
...
if (fscanf(stream,"%d ",&x))
if else (fscanf(stream,"%d:%g",&y,&z))
...
Is there a way to tell scanf to only read a character if it is followed by a space?
The *scanf family of functions do not allow you to do that natively. Of course, you can workaround the problem by reading in the minimum number of elements that you know will be present per input line, validate the return value of *scanf and then proceed incrementally, one item at a time, each time checking the return value for success/failure.
if (1 == fscanf(stream, "%d", &x) && (x == 'desired_value)) {
/* we need to read in some more : separated numbers */
while (2 == fscanf(stream, "%d:%d", &y, &z)) { /* loop till we fail */
printf("read: %d %d\n", y, z);
} /* note we do not handle the case where only one of y and z is present */
}
Your best bet to handle this is to read in a line using fgets and then parse the line yourself using sscanf.
if (NULL != fgets(stream, line, MAX_BUF_LEN)) { /* read line */
int nitems = tokenize(buf, tokens); /* parse */
}
...
size_t tokenize(const char *buf, char **tokens) {
size_t idx = 0;
while (buf[ idx ] != '\0') {
/* parse an int */
...
}
}
char line[MAXLEN];
while( fgets(line,MAXLEN,stream) )
{
char *endptr;
strtol(line,&endptr,10);
if( *endptr==':' )
printf("only y:z <%s>",line);
else
printf("beginning x <%s>",line);
}
I found a crude way to do, what I wanted without having to switch to fgets (which would probably be safer on the long run).
if (fscanf(stream,"%d ",&x)){...}
else if (fscanf(stream,"%d:%g",&y,&z)){...}
else if (fscanf(stream,":%g",&z)){
y=x;
x=0;
}