Related
I wanted to know if there was a way to use scanf so I can take in an unknown number of string arguments and put them into a char* array. I have seen it being done with int values, but can't find a way for it to be done with char arrays. Also the arguments are entered on the same line separated by spaces.
Example:
user enters hello goodbye yes, hello gets stored in array[0], goodbye in array[1] and yes in array[2]. Or the user could just enter hello and then the only thing in the array would be hello.
I do not really have any code to post, as I have no real idea how to do this.
You can do something like, read until the "\n" :
scanf("%[^\n]",buffer);
you need to allocate before hand a big enough buffer.
Now go through the buffer count the number of words, and allocate the necessary space char **array = ....(dynamic string allocation), go to the buffer and copy string by string into the array.
An example:
int words = 1;
char buffer[128];
int result = scanf("%127[^\n]",buffer);
if(result > 0)
{
char **array;
for(int i = 0; buffer[i]!='\0'; i++)
{
if(buffer[i]==' ' || buffer[i]=='\n' || buffer[i]=='\t')
{
words++;
}
}
array = malloc(words * sizeof(char*));
// Using RoadRunner suggestion
array[0] = strtok (buffer," ");
for(int w = 1; w < words; w++)
{
array[w] = strtok (NULL," ");
}
}
As mention in the comments you should use (if you can) fgets instead fgets(buffer,128,stdin);.
More about strtok
If you have an upper bound to the number of strings you may receive from the user, and to the number of characters in each string, and all strings are entered on a single line, you can do this with the following steps:
read the full line with fgets(),
parse the line with sscanf() with a format string with the maximum number of %s conversion specifiers.
Here is an example for up to 10 strings, each up to 32 characters:
char buf[400];
char s[10][32 + 1];
int n = 0;
if (fgets(buf, sizeof buf, sdtin)) {
n = sscanf("%32s%32s%32s%32s%32s%32s%32s%32s%32s%32s",
s[0], s[1], s[2], s[3], s[4], s[5], s[6], s[7], s[8], s[9]));
}
// `n` contains the number of strings
// s[0], s[1]... contain the strings
If the maximum number is not known of if the maximum length of a single string is not fixed, or if the strings can be input on successive lines, you will need to iterate with a simple loop:
char buf[200];
char **s = NULL;
int n;
while (scanf("%199s", buf) == 1) {
char **s1 = realloc(s, (n + 1) * sizeof(*s));
if (s1 == NULL || (s1[n] = strdup(buf)) == NULL) {
printf("allocation error");
exit(1);
}
s = s1;
n++;
}
// `n` contains the number of strings
// s[0], s[1]... contain pointers to the strings
Aside from the error handling, this loop is comparable to the hard-coded example above but it still has a maximum length for each string. Unless you can use a scanf() extension to allocate the strings automatically (%as on GNU systems), the code will be more complicated to handle any number of strings with any possible length.
You can use:
fgets to read input from user. You have an easier time using this instead of scanf.
malloc to allocate memory for pointers on the heap. You can use a starting size, like in this example:
size_t currsize = 10
char **strings = malloc(currsize * sizeof(*strings)); /* always check
return value */
and when space is exceeded, then realloc more space as needed:
currsize *= 2;
strings = realloc(strings, currsize * sizeof(*strings)); /* always check
return value */
When finished using the requested memory from malloc() and realloc(), it's always to good to free the pointers at the end.
strtok to parse the input at every space. When copying over the char * pointer from strtok(), you must also allocate space for strings[i], using malloc() or strdup.
Here is an example I wrote a while ago which does something very similar to what you want:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define INITSIZE 10
#define BUFFSIZE 100
int
main(void) {
char **strings;
size_t currsize = INITSIZE, str_count = 0, slen;
char buffer[BUFFSIZE];
char *word;
const char *delim = " ";
int i;
/* Allocate initial space for array */
strings = malloc(currsize * sizeof(*strings));
if(!strings) {
printf("Issue allocating memory for array of strings.\n");
exit(EXIT_FAILURE);
}
printf("Enter some words(Press enter again to end): ");
while (fgets(buffer, BUFFSIZE, stdin) != NULL && strlen(buffer) > 1) {
/* grow array as needed */
if (currsize == str_count) {
currsize *= 2;
strings = realloc(strings, currsize * sizeof(*strings));
if(!strings) {
printf("Issue reallocating memory for array of strings.\n");
exit(EXIT_FAILURE);
}
}
/* Remove newline from fgets(), and check for buffer overflow */
slen = strlen(buffer);
if (slen > 0) {
if (buffer[slen-1] == '\n') {
buffer[slen-1] = '\0';
} else {
printf("Exceeded buffer length of %d.\n", BUFFSIZE);
exit(EXIT_FAILURE);
}
}
/* Parsing of words from stdin */
word = strtok(buffer, delim);
while (word != NULL) {
/* allocate space for one word, including nullbyte */
strings[str_count] = malloc(strlen(word)+1);
if (!strings[str_count]) {
printf("Issue allocating space for word.\n");
exit(EXIT_FAILURE);
}
/* copy strings into array */
strcpy(strings[str_count], word);
str_count++;
word = strtok(NULL, delim);
}
}
/* print and free strings */
printf("Your array of strings:\n");
for (i = 0; i < str_count; i++) {
printf("strings[%d] = %s\n", i, strings[i]);
free(strings[i]);
strings[i] = NULL;
}
free(strings);
strings = NULL;
return 0;
}
I'm using fgets to read a string in a char array, and then I want to move the pointer over 5 indices and copy the rest of the array into a separate array, BUT I don't want it to copy a newline character; so I've got something like this:
char str1[45], str2[50];
fgets(str2, 50, stdin);
*str2 += 5;
sprintf(str1, "%[^\n]s", str2);
but when I try to compile, I get an error that says:
unknown conversion type character â[â in format [-Wformat]
I'm pretty sure I've used the "%[^\n]s" before with scanf, and it worked just fine, any suggestions?
The pattern %[^n]s is valid format for scanf but it is not a valid format specifier for printf (or sprintf).
Additionally, *str2 += 5 does not skip the first 5 characters (as it appears to be the intention) but instead adds 5 for the byte stored in the first element of str2. str2 = str2 + 5 will not compile since str2 is an array. You could assign the result to a temporary or pass it directly to sprintf.
Here is a slightly better way of doing what you are asking:
size_t len;
char *str1 = NULL, str2[50];
fgets(str2, 50, stdin);
len = strlen(str2);
if (len > 5) {
if (str2[len-1] == '\n') {
str2[len-1] = '\0'; // remove newline
len--;
}
str1 = str2 + 5;
len -= 5;
}
if (str1 != NULL) {
// do stuff
}
Try removing the final "s" in "%[^\n]s"
"%[^\n]s" is ok for scanf(), but not with printf(). Note: certainly the "s" is superfluous.
Various methods exist to trim the trailing \n. Suggest
if (fgets(str2, 50, stdin) == NULL) HAnlde_EOForIOError();
size_t len = strlen(str2);
if (len > 0 && str2[len-1] == '\n') len--;
if (len < 5) Handle_ShortString();
memcpy(str1, str2 + 5, len-5+1);
Note that strings returned from fegts() do not always end in '\n'.
See trimming-fgets
To solve this problem properly, you must not lose sight of the fact that you're dealing with C arrays that have a limited size. The copy must not only stop at the newline, as required, but must ensure that the target array is properly null terminate, and that it is not overrun.
For this, it may be best to write a function, like:
#include <string.h>
/* Copy src to dst, until the point that a character from the bag set
* is encountered in src.(That character is not included in the copy.
* Ensures that dst is null terminated, unless dstsize is zero.
* dstsize gives the size of the dst.
* Returns the number of characters required to perform a complete copy;
* if this exceeds dstsize, then the copy was truncated.
*/
size_t copyspan(char *dst, size_t dstsize, const char *src, const char *bag)
{
size_t ideal_length = strcspn(src, bag); /* how many chars to copy */
size_t limited_length = (ideal_length < dstsize) ? ideal_length : dstsize - 1;
if (dstsize > 0) {
memcpy(dst, src, limited_length);
dst[limited_length] = 0;
}
return ideal_length + 1;
}
With this function we can now do:
if (copyspan(str1, str2, "\n") > sizeof str1) {
/* oops, truncated: handle this somehow */
}
Of course, there is also the issue that fgets may have truncated the original data already.
Dealing with just the trailing newline that is usually returned by fgets (except in the case of an overflowing line or a file not terminated by a newline) is usually done like this:
{
char line[128];
/*...*/
if (fgets(line, sizeof line, file)) {
char *pnl = strchr(line, '\n'); /* obtain pointer to first newline */
if (pnl != 0) /* if found, overwrite it with null */
*pnl = 0;
}
/*...*/
}
If you are doing this kind of line reading in many places, it is better to make a wrapper than to repeat this logic, of course.
#include <stdio.h>
int main(void){
char str1[45], str2[50];
if(fgets(str2, sizeof str2, stdin)){
int i=0, j;
for(j=0; str2[j] && str2[j] != '\n' && j < 5; ++j)
;
if(j == 5){//move success
while(str2[j] && str2[j] != '\n')
str1[i++] = str2[j++];
str1[i]=0;
puts(str1);
}
}
return 0;
}
Personally, I would just implement it from scratch with a simple for loop.
char str1[45], str2[50];
fgets(str2, 50, stdin);
size_t len = strlen(str2);
for (size_t k = 5; k < len; k += sizeof(char)) {
str1[k - 5] = str2[k];
}
I'm struggling with memory allocation. I wanted to input a string into another and I made two functions that stop working at the same place - realloc. These functions are very similar. In first one I copy char by char into a temporary string and when I try to copy temporary string to the first one is the place where I get errors. In the second function I copy the end of first string (from the given position) to a temporary string, reallocate the first string (this is where I get errors) and remove everything in i from the given position. Then I append second string and temporary to a first string. Here is my code.
First function:
// str2 - is a string that I want to input in first string(str)
// at certain position (pos)
void ins (char **str, char *str2, int pos)
{
// lenght of first and second strings
int len = strlen(str[0]),
len2 = strlen(str2),
i, j, l = 0;
// creating temporary string
char *s = (char *) malloc ((len + len2) * sizeof(char));
// copying first part of first string
for (i = 0; i < pos; i++)
s[i] = str[0][i];
// copying second string
for (j = 0; j < len2; j++)
s[i + j] = str2[j];
// copying second part of first string
for (int k = pos; k < len; k++)
{
s[i + j + l] = str[0][k];
l++;
}
// reallocating additional space for second string
// and copying temporary string to first string
str[0] = (char *) realloc (str[0], (len + len2) * sizeof(char));
strcpy(str[0], s);
free(s);
s = NULL;
}
Second function:
void ins2 (char **str,char *str2, int pos)
{
// lenght of first and second string
int len = strlen(str[0]),
len2 = strlen(str2);
// creating a temporary string and copying
// from the given position
char *s = (char *) malloc ((len - pos) * sizeof(char));
strcpy(s, str[0] + pos);
// reallocating space for string that will be added
// deleting part of it from the given position
str[0] = (char *) realloc(str[0], (len + len2) * sizeof(char));
str[0][pos] = '\0';
// adding second string and temporary string
strcat(str[0], str2);
strcat(str[0], s);
// be free, temporary string
free(s);
s = NULL;
}
If you're doing what I think you're trying to do, you need one realloc() for this, assuming the incoming string is indeed already dynamically allocated (it better be):
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
void ins (char **str, const char *str2, size_t pos)
{
// lenght of first and second strings
size_t len = strlen(*str);
size_t len2 = strlen(str2);
// reallocate new string
char *tmp = realloc(*str, len + len2 + 1);
if (tmp != NULL)
{
*str = tmp;
memmove(tmp+pos+len2, tmp+pos, len-pos);
memcpy(tmp+pos, str2, len2);
tmp[len+len2] = 0;
}
}
int main()
{
char *str = strdup("A simple string");
char s2[] = "inserted ";
printf("%s\n", str);
ins(&str, s2, 9);
printf("%s\n", str);
free(str);
return 0;
}
Output
A simple string
A simple inserted string
How It Works
The passed-in strings are both sent through strlen() to obtain their lengths. Once we have those we know how large the resulting buffer needs to be.
Once we realloc() the buffer, the original content is preserved, but we need to (possibly) shift content of the first string to open a hole for the second string. That shift, if done, may require overlapped memory be moved (as it does in the sample). For such memory copying, memmove() is used. Unlike memcpy(), the memmove() library function supports copying where the source and destination regions may overlap.
Once the hole is made, we memcpy() the second string into position. There is no need for strcpy() since we already know the length.
We finish by tacking the last slot to a terminating 0, thereby finishing the null-terminated string and completing the operation
Note I made no affordances at all for this regarding someone passing an invalid pos (out of range), NULL strings, optimizing to nothing if str2 is empty (or NULL), etc. That cleanup I leave to you, but I hope the idea of how this can be done is clear.
This question already has answers here:
Reading strings in C
(6 answers)
Closed 9 years ago.
I want to take an input in c and don't know the array size.
please suggest me the ways how to do this..
hello this is
a sample
string to test.
malloc is one way:
char* const string = (char*)malloc( NCharacters ); // allocate it
...use string...
free(string); // free it
where NCharacters is the number of characters you need in that array.
If you're writing the code yourself, the answer will involve malloc() and realloc(), and maybe strdup(). You're going to need to read the strings (lines) into a large character array, then copy the strings (with strdup()) into a dynamically sized array of character pointers.
char line[4096];
char **strings = 0;
size_t num_strings = 0;
size_t max_strings = 0;
while (fgets(line, sizeof(line), stdin) != 0)
{
if (num_strings >= max_strings)
{
size_t new_number = 2 * (max_strings + 1);
char **new_strings = realloc(strings, new_number * sizeof(char *));
if (new_strings == 0)
...memory allocation failed...handle error...
strings = new_strings;
max_strings = new_number;
}
strings[num_strings++] = strdup(line);
}
After this loop, there's enough space for max_strings, but only num_strings are in use. You could check that strdup() succeeded and handle a memory allocation error there too, or you can wait until you try accessing the values in the array to spot that trouble. This code exploits the fact that realloc() allocates memory afresh when the 'old' pointer is null. If you prefer to use malloc() for the initial allocation, you might use:
size_t num_strings = 0;
size_t max_strings = 2;
char **strings = malloc(max_strings * sizeof(char *));
if (strings == 0)
...handle out of memory condition...
If you don't have strdup() automatically, it is easy enough to write your own:
char *strdup(const char *str)
{
size_t length = strlen(str) + 1;
char *target = malloc(length);
if (target != 0)
memmove(target, str, length);
return target;
}
If you are working on a system with support for POSIX getline(), you can simply use that:
char *buffer = 0;
size_t buflen = 0;
ssize_t length;
while ((length = getline(&buffer, &buflen, stdin)) != -1) // Not EOF!
{
…use string in buffer, which still has the newline…
}
free(buffer); // Avoid leaks
Thank you for the above answers. I have found out the exact answer that I wanted. I hope it will help other people's questions also.
while ((ch == getchar()) != '$')
{
scanf("%c", &ch);
}
I'm wondering if there's a cleaner and more efficient way of doing the following strncpy considering a max amount of chars. I feel like am overdoing it.
int main(void)
{
char *string = "hello world foo!";
int max = 5;
char *str = malloc (max + 1);
if (str == NULL)
return 1;
if (string) {
int len = strlen (string);
if (len > max) {
strncpy (str, string, max);
str[max] = '\0';
} else {
strncpy (str, string, len);
str[len] = '\0';
}
printf("%s\n", str);
}
return 0;
}
I wouldn't use strncpy for this at all. At least if I understand what you're trying to do, I'd probably do something like this:
char *duplicate(char *input, size_t max_len) {
// compute the size of the result -- the lesser of the specified maximum
// and the length of the input string.
size_t len = min(max_len, strlen(input));
// allocate space for the result (including NUL terminator).
char *buffer = malloc(len+1);
if (buffer) {
// if the allocation succeeded, copy the specified number of
// characters to the destination.
memcpy(buffer, input, len);
// and NUL terminate the result.
buffer[len] = '\0';
}
// if we copied the string, return it; otherwise, return the null pointer
// to indicate failure.
return buffer;
}
Firstly, for strncpy, "No null-character is implicitly appended to the end of destination, so destination will only be null-terminated if the length of the C string in source is less than num."
We use memcpy() because strncpy() checks each byte for 0 on every copy. We already know the length of the string, memcpy() does it faster.
First calculate the length of the string, then decide on what to allocate and copy
int max = 5; // No more than 5 characters
int len = strlen(string); // Get length of string
int to_allocate = (len > max ? max : len); // If len > max, it'll return max. If len <= max, it'll return len. So the variable will be bounded within 0...max, whichever is smaller
char *str = malloc(to_allocate + 1); // Only allocate as much as we need to
if (!str) { // handle bad allocation here }
memcpy(str,string,to_allocate); // We don't need any if's, just do the copy. memcpy is faster, since we already have done strlen() we don't need strncpy's overhead
str[to_allocate] = 0; // Make sure there's a null terminator
Basically you're reinventing the strlcpy that was introduced in 1996 - see the strlcpy and strlcat - consistent, safe, string copy and concatenation paper by Todd C. Miller and Theo de Raadt. You might have not heard about it because it was refused to be added to glibc, called “horribly inefficient BSD crap” by the glibc maintainer and fought to this day even when adopted by all other operating systems - see the Secure Portability paper by Damien Miller (Part 4: Choosing the right API).
You can use strlcpy on Linux using the libbsd project (packaged on Debian, Ubuntu and other distros) or by simply copying the source code easily found on the web (e.g. on the two links in this answer).
But going back to your question on what would be most efficient in your case, where you're not using the source string length here is my idea based on the strlcpy source from OpenBSD at http://cvsweb.openbsd.org/cgi-bin/cvsweb/src/lib/libc/string/strlcpy.c?rev=1.11 but without checking the length of the original string, which may potentially be very long but still with proper '\0' ending:
char *d = str; // the destination in your example
const char *s = string; // the source in your example
size_t n = max; // the max length in your example
/* Copy as many bytes as will fit */
if (n != 0) {
while (--n != 0) {
if ((*d++ = *s++) == '\0')
break;
}
}
/* Not enough room in dst, add NUL */
if (n == 0) {
if (max != 0)
*d = '\0'; /* NUL-terminate dst */
}
Here is a version of strlcpy on http://cantrip.org/strlcpy.c that uses memcpy:
/*
* ANSI C version of strlcpy
* Based on the NetBSD strlcpy man page.
*
* Nathan Myers <ncm-nospam#cantrip.org>, 2003/06/03
* Placed in the public domain.
*/
#include <stdlib.h> /* for size_t */
size_t
strlcpy(char *dst, const char *src, size_t size)
{
const size_t len = strlen(src);
if (size != 0) {
memcpy(dst, src, (len > size - 1) ? size - 1 : len);
dst[size - 1] = 0;
}
return len;
}
Which one would be more efficient I think depends on the source string. For very long source strings the strlen may take long and if you don't need to know the original length then maybe the first example would be faster for you.
It all depends on your data so profiling on real data would the only way to find out.
You can reduce the volume of code by:
int main(void)
{
char *string = "hello world foo!";
int max = 5;
char *str = malloc(max + 1);
if (str == NULL)
return 1;
if (string) {
int len = strlen(string);
if (len > max)
len = max;
strncpy(str, string, len);
str[len] = '\0';
printf("%s\n", str);
}
return 0;
}
There isn't much you can do to speed the strncpy() up further. You could reduce the time by using:
char string[] = "hello world foo!";
and then avoid the strlen() by using sizeof(string) instead.
Note that if the maximum size is large and the string to be copied is small, then the fact that strncpy() writes a null over each unused position in the target string can really slow things down.
strncpy() will automatically stop once it hits a NUL; passing max without checking is enough.
I believe this is sufficient:
char *str = malloc(max+1);
if(! str)
return 1;
int len = strlen(string);
memset(str, 0, max+1);
int copy = len > max ? max : len;
strncpy(str, string, copy);