Tek-Tips is the largest IT community on the Internet today!

Members share and learn making Tek-Tips Forums the best source of peer-reviewed technical information on the Internet!

  • Congratulations SkipVought on being selected by the Tek-Tips community for having the most helpful posts in the forums last week. Way to Go!

Dynamic input of strings 2

Status
Not open for further replies.

WebDrake

Programmer
Sep 29, 2005
106
0
0
PL
Hello all,

I've been trying to work out how to dynamically input strings---i.e. to be able to input a string without first deciding what size it should be. Here's a test program:

Code:
/* iotest.c */

#include <stdio.h>
#include <stdlib.h>

int main(void) {
	int i, stringsize;
	char info1[127], info2[127];
	char *info;

	sprintf(info1,"This is");
	sprintf(info2,"a very silly string.");

	printf("%s %s\n",info1,info2); fflush(stdout);
	stringsize = asprintf(&info,"%s %s",info1,info2);
	printf("%s\n",info); fflush(stdout);

	for(i=0;i<stringsize;++i)
		printf("%c",info[i]);
	printf("\nSize = %d\n\n",stringsize);
	
	free(info);

	printf("Please enter an arbitrary string: ");
	scanf("%as",&info);
	stringsize = asprintf(&info,"%s",info);

	printf("%s\n",info); fflush(stdout);
	for(i=0;i<stringsize;++i)
		printf("%c",info[i]);
	printf("\nSize = %d\n\n",stringsize);
	
	free(info);

	return 0;
}

Now, if I just use [tt]gcc iotest.c -o iotest[/tt], everything is fine, and it runs great. But stricter control of dialect involves problems...

e.g. [tt]gcc -ansi -pedantic -Wall iotest.c -o iotest[/tt] produces,

[tt]iotest.c: In function ‘main’:
iotest.c:13: warning: implicit declaration of function ‘asprintf’
iotest.c:23: warning: ISO C does not support the 'a' scanf flag[/tt]

While if I instead use [tt]gcc -std=gnu89 -Wall iotest.c -o iotest[/tt], I get,

[tt]iotest.c: In function ‘main’:
iotest.c:13: warning: implicit declaration of function ‘asprintf’[/tt]

The first I kind of expect since AFAIK the extras I'm using here are GNU extensions, but why does an error still come up when requesting the gnu89 standard? Using [tt]gcc -std=gnu99 -Wall iotest.c -o iotest[/tt] is even worse:

[tt]iotest.c: In function ‘main’:
iotest.c:13: warning: implicit declaration of function ‘asprintf’
iotest.c:23: warning: format ‘%a’ expects type ‘float *’, but argument 2 has type ‘char **’[/tt]

Anyway, my questions are... (1) what options do I need to not get an objection to what I'm doing here? And (2) is there a better way of achieving this goal, such that I can be compatible with the strict [tt]-ansi -pedantic[/tt] compatibility that I like to use?

Thanks in advance. :)
 
> gcc -ansi -pedantic -Wall iotest.c -o iotest
Well I would want to avoid the 'a' modifier anyway, because it has variable meaning (as evidenced by iotest.c:23: warning: format ‘%a’ expects type ‘float *’, but argument 2 has type ‘char **)
Code:
       a      (glibc) Indicates that the conversion will be s, the needed memory space  for  the
              string  will  be  malloc’ed   and  the  pointer to it will be assigned to the char
              pointer variable, which does not have to be initialized before.   This  flag  does
              not exist in ANSI C (C89) and has a different meaning in C99.

> gcc -std=gnu89 -Wall iotest.c -o iotest
You need to manually define the _GNU_SOURCE in your source code (or -D on the command line)
Code:
       #define _GNU_SOURCE
       #include <stdio.h>

       int asprintf(char **strp, const char *fmt, ...);
See "3.7 Predefined Macros" of the manual for which macros get defined.

If you're OK with C99, then snprintf() is pretty close, in that it doesn't overflow the buffer, but does tell you enough to be able to allocate the right amount of space should it fail (so you can try again).

> And (2) is there a better way of achieving this goal
fgets() to a fixed sized buffer
malloc() to allocate the space
strcpy() to copy the fixed buffer to the allocated space

--
 
Salem said:
And (2) is there a better way of achieving this goal
fgets() to a fixed sized buffer
malloc() to allocate the space
strcpy() to copy the fixed buffer to the allocated space

OK, so you would do something like,
Code:
{
    char query[15];
    fgets(query,15,stdin);
}

... ?

Thanks! :)
 
OK. Last off, slightly different but related. Suppose I have in my code,

Code:
{
   int a,b;
   printf("Please enter a number: ");
   scanf("%d",&a);
   fflush(stdin);
   printf("Please enter another number: ");
   scanf("%d",&b);
}

Now, if I run this and in response to the first query type, [tt]3 4[/tt], the second scanf command will pick up on the 4 and set b to that value---despite the fflush(stdin) command.

Is there any way of really clearing previous input so as to avoid this happening?
 
> fflush(stdin);
Probably because it does nothing on your system.

Using scanf() is probably the wrong thing to do, and using it in conjunction with fgets() is just grief.

Use fgets() to read every line
Use sscanf() to extract what you need from each line.


--
 
OK, thanks. :)

For [tt]fgets()[/tt] the usage would be,

Code:
{
   char *query;
   int nchar = 16;
   fgets(query,nchar,stdin);
}

... right? So what happens if I type,

[tt]abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ...[/tt]

Does it simply ignore the extra characters in the string, or will the next [tt]fgets()[/tt] command pick them up?

The other way, I suppose, is to use [tt]getchar()[/tt] to read each character one by one until [tt]'\n'[/tt] is reached, and then read what is wanted from the resulting string.
 
> ... right? So what happens if I type,
No, query is not allocated.
Use a char buff[BUFSIZ];

> or will the next fgets() command pick them up?
Anything which doesn't fit in the current buffer will be left until the next input call.
It's pretty easy to deal with, just call strchr(buff,'\n') on the return result to see if there is a newline. If there is no newline, then the user has typed in a very long line.

--
 
OK. I messed around a bit and came up with the following code to get an arbitrary string:

Code:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#define MYBUFSIZ 17
#define MAXSTRINGLENGTH 2*MYBUFSIZ

char *readLINE(void) {
	unsigned int linesize = 0;
	char query[MYBUFSIZ],*line,*check;
	
	do {
		fgets(query,MYBUFSIZ,stdin);
		check = strchr(query,'\n');
		if(check)
			*check = '\0';
		linesize += strlen(query);
		if(linesize < MAXSTRINGLENGTH) {
			if(linesize>strlen(query)) {
				line = realloc(line,linesize*sizeof(*line));
				strcat(line,query);
			} else {
				line = malloc(linesize*sizeof(*line));
				strcpy(line,query);
			}
		}
	} while(check==NULL);

	return line;
}

I've used [tt]MYBUFSIZ[/tt] for the sake of an example (it makes it easier to test if the buffer size is short:)). I note that having [tt]MAXSTRINGLENGTH = k*MYBUFSIZ[/tt] for some whole number k is necessary to avoid losing data less than the maximum size: in fact it results in a practical maximum of
[tt](k*MYBUFSIZ) - (k-1)[/tt]
including the final [tt]'\0'[/tt] character.

Thoughts? :)

Again, thanks very much for the useful advice; I learned quite a bit today.
 
Actually in practise I do that. I was simplifying for the sake of demonstration. :)
 
Status
Not open for further replies.

Part and Inventory Search

Sponsor

Back
Top