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.
Related
I've tried to run this:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
char a[1000];
void eliminatesp() {
char buff1[1000], buff2[1000];
LOOP: sscanf(a,"%s %s",buff1,buff2);
sprintf(a,"%s%s", buff1, buff2);
for(int i=0; i<strlen(a); ++i) {
if(a[i]==' ') goto LOOP;
}
}
void eliminateline() {
char buff1[1000]; char buff2[1000];
LOOP: sscanf(a,"%s\n\n%s",buff1,buff2);
sprintf(a,"%s\n%s", buff1, buff2);
for(int i=0; i<strlen(a)-1; ++i) {
if(a[i]=='\n'&&a[i+1]=='\n') goto LOOP;
}
}
int main() {sprintf(a,"%s\n\n%s", "hello world","this is my program, cris");
eliminatesp();
eliminateline();
printf("%s",a); return 0;
return 0;
}
but the output was:
hello world
world
How can I correct it? I was trying to remove spaces and empty lines.
Going with your idea of using sscanf and sprintf you can actually eliminate both spaces and newlines in a single function, as sscanf will ignore all whitespace (including newlines) when reading the input stream. So something like this should work:
void eliminate() {
char buff1[1000], buff2[1000], b[1000];
char* p = a, *q = b, *pq = b;
sprintf(q, "%s", p);
while (q != NULL && *q != '\0')
{
if (iswspace(*q))
{
sscanf(pq, "%s %s", buff1, buff2);
sprintf(p, "%s%s", buff1, buff2);
p += strlen(buff1);
pq = ++q;
}
q++;
}
}
Pedro, while the %s format specifier does stop conversion on the first encountered whitespace, it isn't the only drawback to attempting to parse with sscanf. In order to use sscanf you will also need to use the %n conversion specifier (the number of characters consumed during conversion to the point the %n appears) and save the value as an integer (say offset). Your next conversion will begin a a + offset until you have exhausted all words in 'a'. This can be a tedious process.
A better approach can simply be to loop over all characters in 'a' copying non-whitespace and single-delimiting whitespace to the new buffer as you go. (I often find it easier to copy the full string to a new buffer (say 'b') and then read from 'b' writing the new compressed string back to 'a').
As you work your way down the original string, you use simple if else logic to determine whether to store the current (or last) character or whether to just skip it and get the next. There are many ways to do this, no one way more right than the other as long as they are reasonably close in efficiency. Making use of the <ctype.h> functions like isspace() makes things easier.
Also, in your code, avoid the use of global variables. There is no reason you can't declare 'a' in main() and pass it as a parameter to your eliminate functions. If you need a constant in your code, like 1000, then #define a constant and avoid sprinkling magic numbers throughout your code.
Below is an example putting all those pieces together, and combining both your eliminatesp and eliminateline functions into a single eliminatespline function that does both trim whitespace and eliminate blank lines. This will handle blank lines and considers lines containing only whitespace characters as blank.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#define MAXL 1000 /* if you need a constant, define one (or more) */
/** trim leading, compress included, and trim trailing whitespace.
* given non-empty string 'a', trim all leading whitespace, remove
* multiple included spaces and empty lines, and trim all trailing
* whitespace.
*/
void eliminatespline (char *a)
{
char b[MAXL] = "", /* buffer to hold copy of a */
*rp = b, /* read pointer to copy of a */
*wp = a, /* write pointer for a */
last = 0; /* last char before current */
if (!a || !*a) /* a NULL or empty - return */
return;
strcpy (b, a); /* copy a to b */
while (isspace (*rp)) /* skip leading whitespace */
rp++;
last = *rp++; /* fill last with 1st non-whitespace */
while (*rp) { /* loop over remaining chars in b */
/* last '\n' and current whitespace - advance read pointer */
if (last == '\n' && isspace(*rp)) {
rp++;
continue;
} /* last not current or last not space */
else if (last != *rp || !isspace (last))
*wp++ = last; /* write last, advance write pointer */
last = *rp++; /* update last, advance read pointer */
}
if (!isspace (last)) /* if last not space */
*wp++ = last; /* write last, advance write pointer */
*wp = 0; /* nul-terminate at write pointer */
}
int main() {
char a[] = " hello world\n \n\nthis is my program, cris ";
eliminatespline (a);
printf ("'%s'\n", a);
return 0;
}
note: the line being trimmed has both leading and trailing whitespace as well as embedded blank lines and lines containing only whitespace, e.g.
" hello world\n \n\nthis is my program, cris "
Example Use/Output
$ ./bin/elimspaceline
'hello world
this is my program, cris'
(note: the printf statements wraps the output in single-quotes to confirm all leading and trailing whitespace was eliminated.)
If you did want to use sscanf, you could essentially do the same thing with sscanf (using the %n specifier to report characters consumed) and a array of two-characters to treat the next character as a string, and do something like the following:
void eliminatespline (char *a)
{
char b[MAXL] = "", /* string to hold build w/whitespace removed */
word[MAXL] = "", /* string for each word */
c[2] = ""; /* string made of char after word */
int n = 0, /* number of chars consumed by sscanf */
offset = 0; /* offset from beginning of a */
size_t len; /* length of final string in b */
/* sscanf each word and char that follows, reporting consumed */
while (sscanf (a + offset, "%s%c%n", word, &c[0], &n) == 2) {
strcat (b, word); /* concatenate word */
strcat (b, c); /* concatenate next char */
offset += n; /* update offset with n */
}
len = strlen (b); /* get final length of b */
if (len && isspace(b[len - 1])) /* if last char is whitespace */
b[len - 1] = 0; /* remove last char */
strcpy (a, b); /* copy b to a */
}
Look things over, try both approaches and let me know if you have further questions.
my code would work in this way:
input : a[]="create /dir/bar"
and save in this string:
b[]=create
c[]=/dir/bar
there is also a case in which i save an other string: (for example)
a[]=write /foo/bar "test"
b[]= write
c[]=/foo/bar
d[]=test (without the "")
my code is this :
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#define SPACE ' '
void divide(char a[], char b[], char c[], char d[]);
int main(int argc, char const *argv[]) {
char a[50+1];
char b[50+1];
char c[50+1];
char d[50+1];
int i;
scanf("%s\n", a);
divide(a, b, c, d);
for(i=0; b[i]!='\0'; i++)
printf("%s %s %s \n", b, c, d);
return 0;
}
void divide(char a[], char b[], char c[], char d[]){
int i, j;
for(i=0; a[i]!=SPACE; i++)
b[i]=a[i];
b[i]='\0';
for(; a[i]==SPACE; i++)
;
for(j=0; a[i]!='\0'; i++, j++)
c[j]=a[i];
c[j]='\0';
for(; a[i]==SPACE; i++)
;
if(a[i]=='"'){
i++;
for(j=0; a[i]!='"'; i++)
d[j]=a[i];
d[j]='\0';
return;
}
}
but it does not work for a segmentation fault after the program get the input. where is the problem?
I must not use malloc, because it spend too much time to work (I have to get thousands of these lines) and does not respect a limit. (I work for a project in my university)
You may be making this a little more difficult than it needs to be. Yes, you can tokenize a string by repeated calls to sscanf or with repeated reads with scanf, but the C library provides a tool to tokenize words from a line of text. Smartly enough named strtok.
You simply declare a constant string holding the delimiters you wish to break the words on (e.g. delims = " \t"; to break the words on space or tab, and then call strtok (str, delims) to return the first token (word), and then loop over repeated calls to strtok (NULL, delims) to parse the remaining words (or until you reach your max of 3 words).
(note the first call to strtok uses str as the first parameter, while all subsequent calls use NULL)
This is a far more flexible way to handle an unknown number of tokens in a string.
Instead of using a[], b[], c[], etc.. consider using just a single buf[] to read the line of input into, and then an array of strings to hold the parameters (which allows you to use an index variable during your loops over strtok to assign and copy the correct string to the associated index).
Don't use void as a return in circumstances like this. Why not use a meaningful return (like the number of parameters in the line of text). That way, you know how many were read (or tokenized) in your divide function. Give it a return that can provide useful information, e.g.
size_t divide (char *buf, char (*params)[MAXC+1]);
Which will now return a size_t type containing the number of parameters that result from each call to divide.
Putting it altogether, (and using fgets to read the entire line of input), you could do something like the following:
#include <stdio.h>
#include <string.h>
enum { MAXP = 3, MAXC = 50 }; /* max parameters & chars */
size_t divide (char *buf, char (*params)[MAXC+1]);
int main (void) {
char buf[MAXC * 4 + 1] = "";
char params[MAXP][MAXC + 1]; /* array to hold 3 parameters */
size_t i, len, nparams = 0;
/* use fgets for line-oriented user input */
printf ("\nenter commands: ");
if (!fgets (buf, sizeof buf, stdin)) {
fprintf (stderr, "error: insufficient input.\n");
return 1;
}
len = strlen (buf); /* get length */
if (buf[len - 1] == '\n') /* validate last char is '\n' */
buf[--len] = 0; /* overwrite with nul-terminating char */
else { /* short read -- handle error */
fprintf (stderr, "error: incomplete input read.\n");
return 1;
}
nparams = divide (buf, params);
for (i = 0; i < nparams; i++)
printf ("parameter[%zu] : %s\n", i, params[i]);
return 0;
}
/* divide using strtok */
size_t divide (char *buf, char (*params)[MAXC+1])
{
char *delims = " \t", /* delimiters for strtok */
*p = buf; /* pointer to buf */
size_t n = 0; /* var to return number of params */
p = strtok (buf, delims); /* tokenize fist paramter */
while (p) { /* now loop until all words exhausted or limit reached */
strncpy (params[n++], p, MAXC); /* copy token to params array */
if (n == MAXP) /* check if limit reached */
break;
p = strtok (NULL, delims); /* get next token */
}
return n; /* return the number of parameters found */
}
Example Use/Output
$ /bin/splitparams
enter commands: create /dir/bar
parameter[0] : create
parameter[1] : /dir/bar
$ ./bin/splitparams
enter commands: write /foo/bar "test"
parameter[0] : write
parameter[1] : /foo/bar
parameter[2] : "test"
Or providing a bunch of extra words (to validate handling of only 3)
$ ./bin/splitparams
enter commands: write /foo/bar "test" and some more stuff
parameter[0] : write
parameter[1] : /foo/bar
parameter[2] : "test"
If you run this simple program
#include<stdio.h>
int main(int argc, char const *argv[]) {
char a[50+1];
scanf("%s\n", a);
printf("|%s|\n", a);
return 0;
}
and give the input "create foo", you'll get the output
|create|
As you can see you only got the first word, i.e. "create", instead of the expected "create foo" as
scanf("%s\n", a);
will only give the first word. Consequently your divide function will fail. Instead of scanf you could do
fgets(a, 51, stdin);
to make sure the whole input is read into array a.
In general your program lacks a lot of range checking and input validation. You should add that.
Another problem I see is that in case the input is
create /dir/bar
you never initialize the string d but you still print it in main. That is undefined behaviour.
Try:
char d[50+1];
d[0] = '\0'; // Add this line
The condition is:
I want to input a line from standard input, and I don't know the size of it, maybe very long.
method like scanf, getsneed to know the max length you may input, so that your input size is less than your buffer size.
So Is there any good ways to handle it?
Answer must be only in C, not C++, so c++ string is not what I want. I want is C standard string, something like char* and end with '\0'.
The C standard doesn't define such a function, but POSIX does.
The getline function, documented here (or by typing man getline if you're on a UNIX-like system) does what you're asking for.
It may not be available on non-POSIX systems (such as MS Windows).
A small program that demonstrates its usage:
#include <stdio.h>
#include <stdlib.h>
int main(void) {
char *line = NULL;
size_t n = 0;
ssize_t result = getline(&line, &n, stdin);
printf("result = %zd, n = %zu, line = \"%s\"\n", result, n, line);
free(line);
}
As with fgets, the '\n' newline character is left in the array.
One way is to run a loop with getchar and keep placing the characters into an array. Once the array is full, reallocate it to a larger size.
There is an often overlooked conversion specification within scanf that will allocate memory sufficient to hold the string input regardless of length. Newer versions of scanf use m for this purpose. Older versions used a. For example:
#include <stdio.h>
#include <stdlib.h>
int main (void) {
char *str = NULL;
printf (" enter a string of any length, whitespace is OK: ");
scanf ("%m[^\n]%*c", &str);
printf ("\n str: %s\n\n", str);
if (str) free (str);
return 0;
}
Note: scanf requires a char ** pointer argument to receive the allocated string. Also note scanf does not include the '\n' in the stored string. Further note the %*c which receives and discards the '\n' character to prevent the newline from remaining in the input buffer. You may also precede the conversion specifier with whitespace to skip any/all whitepace that may exist in the input buffer.
Lastly Note: there are reports that not all implementations of scanf offer this feature. (which may also be confusion of the m/a change) Check your implementation.
One of the method is using getchar() function we can get input in a character and transfer it to dynamicall created array. You can see that when it exceeds the default length set by us, we reallocated space for storing character
#include<stdio.h>
#include<stdlib.h>
void main(){
int size = 10;
char* str;
str = (char*) calloc(size,sizeof(char));
char c;
c = getchar();
int t = 0;
int cnt = 0;
int len;
while(c!='\n') {
if(cnt > size) {
str = (char*) realloc(str,2*cnt);
}
str[t] = c;
c = getchar();
t++;
cnt++;
}
str[t]='\0';
printf("The string is %s\n",str);
len = strlen(str);
printf("The size is %d",len);
}
use getchar, malloc and realloc for reading the unlimited input string
Declare String type, you can also use char *
// String type
typedef char *String;
I write this function for joining the char in the end of string
/**
* Join the Char into end of String
*
* #param string - String
* #param c - joined char
*/
void String_joinChar(String *string, const char c)
{
const size_t length = strlen(*string);
(*string) = (String)realloc((*string), sizeof(char) * (length + 2));
(*string)[length] = c;
(*string)[length + 1] = '\0';
}
This function for inputting string, which read the char from keyboard by using getchar and join it in the end of current string.
/**
* Input String
*
* #return Inputed String
*/
String String_input()
{
String string = (String)malloc(sizeof(char));
strcpy(string, "");
char cursor;
fflush(stdin);
while ((cursor = getchar()) != '\n' && cursor != EOF)
{
String_joinChar(&string, cursor);
}
return string;
}
Cause of using char *, malloc and realloc, we must free it
/**
* Destroy String
*
* #param string - Destroyed String
*/
void String_destroy(String string)
{
free(string);
}
And now we just use it !!
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main()
{
String string = String_input();
printf("\n%s\n", string);
String_destroy(string);
return 0;
}
Hope useful to you!
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.
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).