I'm writing a toString method that prints out all attributes of an inputted pointer to a struct. In reading safe methods for handling strings, I finally created the below solution:
note a Person struct has attributes name, weight, height, and age (all ints except for name, which is a char array).
char* toString(struct Person* inputPerson)
{
// Allocate memory to return string
char* ret = malloc(sizeof (char) * 100);
// copy "Name: " into string
strcpy(ret, "Name: ");
// safely copy name, at most leaving enough room for the other params (leaving 50 bytes)
strncat(ret, inputPerson->name, 100-50);
// copy "Age: " into string
strcat(ret, "\n\tAge: ");
// create tmp char to allow us to convert ints age, weight, and height into strings
char tmp[4];
// safely convert string to int (max number of digits being 3)
snprintf(tmp, sizeof(tmp), "%d", inputPerson->age);
// safely copy at most 3 characters, plus a null terminating char
strcat(ret, tmp); // the previous snprintf makes sure that tmp is not too large.
// repeat previous 2 steps for both weight and height attributes
strcat(ret, "\n\tWeight: ");
snprintf(tmp, sizeof(tmp), "%d", inputPerson->weight);
strcat(ret, tmp);
strcat(ret, "\n\tHeight: ");
snprintf(tmp, sizeof(tmp), "%d", inputPerson->height);
strcat(ret, tmp);
// Return a pointer to the string
return ret;
}
My question is: is this overkill? All I want to do is print out each attribute safely and securely. For each string, I have to make sure it is a maximum size before appending. For each integer, I have to snprintf it into a string (ensuring a maximum allowed length) and then append that string to my return string. Is there a simpler way? Also my heebie jeebies are growing every time I look at the "100-50" code portion: how can I specify "size allocated to ret" instead of 100?
Since you are mallocing the return buffer anyway, why not make life easier on yourself by allocating a buffer of the correct size? As Joachim suggests, you can even do that with snprintf:
char* toString(struct Person* inputPerson)
{
size_t space_required =
snprintf(0, 0,
"Name: %s\n"
"\tAge: %d\n"
"\tWeight: %d\n"
"\tHeight: %d\n",
inputPerson->name,
inputPerson->age,
inputPerson->weight,
inputPerson->height);
// space_required excludes the terminating NUL
// sizeof(char) == 1 *by definition*
char *ret = malloc(space_required+1);
if (!ret)
return 0;
snprintf(ret, space_required+1,
"Name: %s\n"
"\tAge: %d\n"
"\tWeight: %d\n"
"\tHeight: %d\n",
inputPerson->name,
inputPerson->age,
inputPerson->weight,
inputPerson->height);
return ret;
}
That probably seems awful repetitive. If your C library has asprintf, you can avoid the repetition:
char* toString(struct Person* inputPerson)
{
char *ret;
if (asprintf(&ret,
"Name: %s\n"
"\tAge: %d\n"
"\tWeight: %d\n"
"\tHeight: %d\n",
inputPerson->name,
inputPerson->age,
inputPerson->weight,
inputPerson->height) == -1)
return 0;
return ret;
}
If you don't have asprintf, you can implement it using malloc and vsnprintf. I'll leave that as an exercise ;-)
Related
#include <stdio.h>
int main()
{
char in_name[80];
FILE *in_file;
char word[50];
char word2[50];
char word3[50];
char *strs[]= {"foo ABAA", "bar", "bletch","4"};
strcpy(*strs[4],"wow");
in_file = fopen("test2.txt", "r");
if (in_file == NULL)
printf("Can't open %s for reading.\n", in_file);
else
{
while (fscanf(in_file, "%s %s %s ", word,word2,word3) != EOF)
{
printf("%s ", word);
printf("%s ", word2);
printf("%s\n", word3);
}
fclose(in_file);
}
printf("%s",strs[3]);
int result = atoi(strs[3] );
printf("\n");
printf("%d",result);
return 0;
}
Im new in C.Im trying to add the "woo" string to the strs array.But it gives an error.Im open to any help.My main point is reading line by line a txt file and adding every word to a array like {"what", "a", "nice","day"}
There are a few steps you must take:
To read a potentially unlimited number of words from your file, you must have an array strs that can hold enough words. Let's say 50:
char *strs[50]= 0;
But this is an array of pointers to strings; the pointers still point nowhere. So you must have memory to store the strings read. We will allocate the memory after each word read.
You open the file and start reading. For each word read, you allocate the memory and store it in the strs array. Then you copy the word to the memory you have just allocated:
int i= 0;
while (fscanf(in_file, "%s %s %s ", word,word2,word3) != EOF)
{
strs[i]= malloc(strlen(word )+1; strcpy(strs[i],word ); i++;
strs[i]= malloc(strlen(word2)+1; strcpy(strs[i],word2); i++;
strs[i]= malloc(strlen(word3)+1; strcpy(strs[i],word3); i++;
}
fclose(in_file);
Thats's all...
what you did wrong and needs noticing:
char *strs[]= {"foo ABAA", "bar", "bletch","4"};
Ths is an array that you initialize with the values between { and }. You do not specify the size and the size is taken by counting the number of initializers between the { and }.
You initialize the array with literal strings. These are constants and cannot be modified. So when you do:
strcpy(*strs[4],"wow");
you do 4 things wrong: 1) entry 4 is beyond your array; 2) you dereference a pointer (strs[4] is already a pointer, so no * needed), and even if we forget this last error, you try 3) to overwrite a read-only string, and that is not allowed. And finally, 4) the string you try to copy is much longer than the string that was there, so even if the string was not read-only, you make an error.
while (fscanf(in_file, "%s %s %s ", word,word2,word3) != EOF)
This use of scanf is not really wrong but it is always better to compare the result with the number of items you want to read. 3 here. Also, we still must guard against reading too many words:
while (i<47 && fscanf(in_file, "%s %s %s ", word,word2,word3)==3)
I'm trying to read a buffer, copy all the non-alphabetical characters and dump it into another buffer.
The problem I'm getting is that whenever I encounter a non numerical character, it won't copy anything more.
I have tried to try to detect the blank spaces, use an auxiliary buffer...
int main(void)
{
char buffer[] = "103 PLUS 1";
char buffer2[] = "a";
int i, number;
memset(buffer2, 0, sizeof (buffer2));
size_t length = strlen(buffer);
fprintf(stdout,"Buffer initially: %s \n", buffer);
fprintf(stdout,"Buffer2 initially: %s \n", buffer2);
for(i=0; i<length; i++)
{
if (number = isalpha(buffer[i]) == 0)
{
strncpy(&buffer2[i], &buffer[i], 1);
}
}
fprintf(stdout, "Copied buffer is: %s \n", buffer2);
return 0;
}
Thanks in advance.
Your code leaves the first element of buffer2 to be 0, indicating the end of the C-string. In fact, the index of same characters should differ in buffer and buffer2, because characters must be consistent within a C-string, but some characters are not copied.
It's unnecessary to initialise buffer with "a". Use {0} instead.
You don't need to use variable number.
Refined code:
#include <stdio.h>
#include <ctype.h>
#include <string.h>
int main(void)
{
char buffer[80] = "103 PLUS 1";
char buffer2[80] = {0};
int i, j = 0;
size_t length = strlen(buffer);
fprintf(stdout,"Buffer initially: %s \n", buffer);
fprintf(stdout,"Buffer2 initially: %s \n", buffer2);
for(i = 0; i < length; i++)
{
if (isalpha(buffer[i]))
{
strncpy(&buffer2[j++], &buffer[i], 1);
}
}
fprintf(stdout, "Copied buffer is: %s \n", buffer2);
return 0;
}
Output:
Buffer initially: 103 PLUS 1
Buffer2 initially:
Copied buffer is: PLUS
When you skip copying a character over, buffer2 is left w/ whatever was originally in that position, which seems to be 0 (but may not always be), which is why printing it out looks like no other characters were copied. In fact, they were, but the skipped character is interpreted as the null termination of that string.
But worse, you never allocate space for buffer2: you let the compiler do it, which means you aren't really allowed to modify that space. As with the above, you might be getting away with it, but it is undefined behavior, and could actually crash your program.
Specifically, I am trying to build up the following statement and run it as the following command:
system("java -cp . mainpackage.SomeClass 1234567890 98765");
I have researched this online, but the examples did not compile, so I pieced together the following illustrative pseudo code. How do I change the code below to get it to concatenate the string and execute the command?
#include <stdio.h>
main() {
char jv[33];
strcpy(jv, "java -cp . mainpackage.SomeClass ");
char to[10];
strcpy(to, "1234567890 ");
char pin[5];
strcpy(pin, "98765");
system(jv + to + pin);
}
to and pin are arguments sent to a java program. Eventually, to and pin will be coming from inputs, but for now I just want to treat them as these values while I test other pieces of the code.
snprintf() is a flexible and a safe method. #fukanchik. The main challenge in C is to:
1) manage string memory space and
2) prevent overruns.
const char *jv = "java -cp . mainpackage.SomeClass ";
const char *to = "1234567890 ";
const char *pin = "98765";
#define BIGBUF 100
char command[BIGBUF];
snprintf(command, sizeof command, "%s%s%s", jv, to, pin);
system(command);
Or with C99 and assuming integers
const char *jv = "java -cp . mainpackage.SomeClass";
long to = 1234567890;
long pin = 98765;
int n = snprintf(NULL, 0, "%s %ld %ld", jv, to, pin);
char command[n+1];
snprintf(command, sizeof command, "%s %ld %ld", jv, to, pin);
system(command);
From an actual command line, like int main (int argc, char **argv)
char buf[1024] = {0};
int n;
for (n = 0; n < argc; n ++)
strcat (buf, argv[n]); // Change to strlcat if you have it available (BSD or Mac)
printf ("result = %s\n", buf);
Or using your example:
char jv[33];
strcpy(jv, "java -cp . mainpackage.SomeClass ");
char to[10];
strcpy(to, "1234567890 ");
char pin[5];
strcpy(pin, "98765");
system(jv + to + pin);
char result[1024] = {0}; // C does not zero format variables inside a function, so we do it ourselves with = {0}
sprintf (result, "%s %s %s", jv, to, pin); // Should use snprintf instead if available
printf ("result = %s\n", result);
OR
char result[1024] = {0};
strcat (result, jv);
strcat (result, " ");
strcat (result, to);
strcat (result, " ");
strcat (result, pin); // Should use strlcat instead if available
printf ("result = %s\n", result);
First, let me tell you the mistakes in above code.
char jv[33];
strcpy(jv, "java -cp . mainpackage.SomeClass ");
in C, strings are null-terminated, so you'll be needing an extra element per string to store the null. Expand the size of your array.
The, for concatenation like
system(jv + to + pin);
you can define an array large enough to hold the concatenated final value and use strcat() to concatenate the source and destination string. You can use a loop, to append more than one sub-string to make a complete command-string, as you want.
I am a newbie in c programming language and I have a university tutorial assignment that is related with working with chars(I wont be graded for this assignment) where you have to count words, I have to compile and submit my answers in an online web environment where my code will run against test cases that are not visible to me.here is my assignment:
Write the function 'wc' which returns a string containing formatted as follows: "NUMLINES NUMWORDS NUMCHARS NUMBYTES" .
Whitespace characters are blanks, tabs (\t) and new lines (\n). A character is anything that is not whitespace. The given string is null-char (\0) terminated.
here is my code:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
char* wc(char* data) {
char* result ;
int numLine ;
int numWords ;
int numChars ;
int i;
int numBytes =strlen(data);
char* empty=NULL;
while(strstr(data,empty)>0){
numWords=1;
for (i = 0; i < sizeof(data); i++) {
if(data[i]=='\n'){
numLine++;
}
if(data[i]==' ' ){
numWords++;
}
if(data[i]!=' '){
numChars++;
}
}
}
sprintf(result, "%d %d %d %d", numLine, numWords, numChars, numBytes);
return result;
}
this code will give me the correct output result but I am missing something here at least the test tells me that.
You've got a very serious error:
char* result;
...
sprintf(result, "%d %d %d %d", numLine, numWords, numChars, numBytes);
This is not allowed in C. You need to allocate sufficient memory for the string first. Declare result as a large enough static array, or use malloc if you've covered that in your course.
e.g.
char buf[100]; // temporary buffer
sprintf(buf, "%d %d %d %d", numLine, numWords, numChars, numBytes);
char *result = malloc(strlen(buf) + 1); // just enough for the string
strcpy(result, buf); // store the string
return result;
What if you have this input?
Two Words.
You have to count the transitions between whitespace/non-whitespace, not just count spaces.
Also, I'm pretty sure strstr(data,NULL) will not do anything useful.
You also appear to be missing the \t for tab in your white space checker, and you're not correctly checking when you're in or out of a word. You can use the boolean type bool for this defined in stdbool.h for this.
Source code of wc unix command:
http://www.gnu.org/software/cflow/manual/html_node/Source-of-wc-command.html
All test cases handled.
1) sizeof is wrong:
Instead of sizeof operator you need to use strlen() in for loop, like:
for (i = 0; i < strlen(data); i++)
^ not sizeof
sizeof(data) returns only size of data pointer address that is 4. Because you are to read all char in data[] you need strlen() that will return length of data[] (or number of chars in data[])
2) memory error:
Next Error I can notice there is no memory allocated for result. it declare like:
char* result ;
and No memory allocate! and you are writing using sprintf that cause undefined behavior of your code
3) while(strstr(data,empty)>0) is wrong
strstr() search position of a string in to other you empty string is NULL , CHECK:
char *strstr(const char *s1, const char *s2);
you strstr() always returns data, Why are you calling this? I believe you don't need this while() loop.
I improved you code upto some extend as below, There was only three error as I mentioned above now corrected(to understand read comments), You basic algo is correct:
#define SIZE 256 // added size macro
char* wc(char* data)
char* result = malloc(SIZE*sizeof(char)); //(2) allocated memory for result
int numLine ;
int numWords ;
int numChars ;
int i;
int numBytes =strlen(data);
numWords=1;
// (3) remove while loop
for (i = 0; i < strlen(data); i++) { //(1) change size
if(data[i]=='\n'){
numLine++;
}
if(data[i]==' ' ){
numWords++;
}
if(data[i]!=' '){
numChars++;
}
}
sprintf(result, "%d %d %d %d", numLine, numWords, numChars, numBytes);
return result;
}
int main(){
printf("\nresult: %s\n", wc("q toei lxlckmc \t \n ldklkjjls \n i \t nn "));
return 1;
}
Output:
result: 2 14 28 41
char buffer[128]
ret = scanf("%s %s", buffer);
This only allows me to print the first string fed into console. How can I scan two strings?
char buffer[128], buffer2[128];
ret = scanf("%s %s", buffer, buffer2);
if you want to reuse buffer you need two calls to scanf, one for each string.
ret = scanf("%s", buffer);
/* Check that ret == 1 (one item read) and use contents of buffer */
ret = scanf("%s", buffer);
/* Check that ret == 1 (one item read) and use contents of buffer */
If you want to use two buffers then you can combine this into a single call to scanf:
ret = scanf("%s%s", buffer1, buffer2);
/* Check that ret == 2 (two items read) and use contents of the buffers */
Note that reading strings like this is inherently insecure as there is nothing preventing a long string input from the console overflowing a buffer. See http://en.wikipedia.org/wiki/Scanf#Security.
To fix this you should specify the maximum length of the strings to be read in (minus the terminating null character). Using your example of buffers of 128 chars:
ret = scanf("%127s%127s", buffer1, buffer2);
/* Check that ret == 2 (two items read) and use contents of the buffers */
You need to choose two different locations for the first and second strings.
char buffer1[100], buffer2[100];
if (scanf("%99s%99s", buffer1, buffer2) != 2) /* deal with error */;
If you know the number of words you want to read, you can read it as :
char buffer1[128], buffer2[128];
ret = scanf("%s %s", buffer1, buffer2);
Alternatively you can use fgets() function to get multiword strings .
fgets(buffer, 128 , stdin);
See Example
#include<stdio.h>
int main(){
int i = 0,j =0;
char last_name[10]={0};
printf("Enter sentence:");
i=scanf("%*s %s", last_name);
j=printf("String: %s",last_name)-8;
/* Printf returns number of characters printed.(String: )is adiitionally
printed having total 8 characters.So 8 is subtracted here.*/
printf("\nString Accepted: %d\nNumber of character in string: %d",i,j);
return 0;
}