I've been working on a function which censors words. I've created this function and it works, except that it only accepts one argument. For example, when I do "./a.out test < example.txt", it replaces "test" with CENSORED in "example.txt". But when I add another one, like "./a.out test test2 < example.txt", it only replaces "test", not both "test" and "test2".
Can someone help me with my function please? I don't know what I'm doing wrong.
Here's the function:
#include <assert.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main(int argc, char **argv)
{
assert(argc > 1);
char fileRead[4096];
char replace[] = "CENSORED";
size_t word_len = strlen(argv[1]);
while (fgets(fileRead, sizeof(fileRead), stdin) != 0)
{
char *start = fileRead;
char *word_at;
while ((word_at = strstr(start, argv[1])) != 0)
{
printf("%.*s%s", (int)(word_at - start), start, replace);
start = word_at + word_len;
}
printf("%s", start);
}
printf("\n");
return (0);
}
I suggest you put in a middle loop over argv elements 1 to argc - 1. In each iteration, it would process the line to censor the corresponding argument, just as your function now does only for the first argument.
int arg;
for (arg = 1; arg < argc; arg += 1) {
/* ... censor argv[arg] ... */
}
Note that you can then drop the assert(), too, as structuring the argument access that way naturally avoids accessing the argv array outside its bounds (if no arguments are specified, the program will run without censoring anything).
Related
I am trying to use va_list & its associated macros with vsprintf() to create a format string that has a variable number of specifiers. Here is an example program I wrote in which the number of specifiers can only be altered via the NUM_ARG macro:
#include <stdio.h>
#include <stdlib.h>
#include <stdarg.h>
#include <string.h>
#define MAXBUF 4096
#define SPECIFIER "(%s)"
#define NUM_ARG 5
char *strmaker(int num_args, ...)
{
char form[MAXBUF] = { [0] = '\0' };
char *prnt = (char *) malloc(sizeof(char) * MAXBUF);
va_list strings;
for (int i = 0; i < num_args; ++i)
strcat(form, SPECIFIER);
va_start(strings, num_args);
vsprintf(prnt, form, strings);
va_end(strings);
return prnt;
}
int main(int argc, char *argv[])
{
if (argc != (NUM_ARG + 1))
return -1;
char *s = strmaker(NUM_ARG, argv[1], argv[2], argv[3], argv[4], argv[5]);
printf("%s\n", s);
free(s);
return 0;
}
However, this isn't exactly what I want to achieve. How could I do this with a variable number of arguments? How could a variable number of strings be passed to a function and used to initialise a va_list?
As far as I know, it is not possible to do that. If you are not so keen about using variadic functions and can redefine the function. The below code suits your need; Iterate through each item in the array and append to the string using snprintf.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define MAXBUF 4096
#define SPECIFIER "(%s)"
char *strmaker(int num_args, char** strings)
{
char *prnt = (char *) malloc(sizeof(char) * MAXBUF);
int cur = 0;
/* Append the strings to the prnt buffer */
for (int i = 0; i < num_args; i++) {
int p_return = snprintf(prnt + cur, MAXBUF - cur, SPECIFIER, strings[i]); // If no error, return the number characters printed excluding nul (man page)
if (p_return >= MAXBUF - cur) // If buffer overflows (man page)
return prnt;
cur = cur + p_return; // Update the index location.
}
return prnt;
}
int main(int argc, char *argv[])
{
if (argc <= 1)
return -1;
char *s = strmaker(argc - 1, argv + 1);
printf("%s\n", s);
free(s);
return 0;
}
Terminal Session:
$ ./a.out 1 2 3
(1)(2)(3)
$ ./a.out 1 2 3 4 5 6 7
(1)(2)(3)(4)(5)(6)(7)
$ ./a.out Hello, This is stackoverflow, Bye
(Hello,)(This)(is)(stackoverflow,)(Bye)
Short answer is: You can't.
However you can work around it by using arrays of strings, possibly dynamically allocated. Then you could basically use the same technique you do now, but iterate over the array instead.
Perhaps something like this:
char *strmaker(size_t count, char *strings[])
{
// First get the length of all strings in the array
size_t result_length = 0;
for (size_t i = 0; i < count; ++i)
{
// +1 for space between the strings
// And for the last string adds space for the string null-terminator
result_length += strlen(strings[i]) + 1;
}
// Now allocate the string (using calloc to initialize memory to zero, same as the string null-terminator)
char *result = calloc(1, result_length);
// And not concatenate all strings in the array into one large string
for (size_t i = 0; i < count; ++i)
{
strcat(result, strings[i]);
if (i != count - 1)
{
strcat(result, " "); // Add space, except after last string
}
}
// Return the resulting string
return string;
}
int main(int argc, char *argv[])
{
// Create an array for all arguments
char **arguments = malloc(sizeof(char *) * argc - 1);
for (int a = 1; a < argc)
{
arguments[a - 1] = argv[a];
}
// Now create the single string
char *result = strmaker(argc - 1, arguments);
// ... and print it
printf("%s\n", result);
// Finally clean up after us
free(result);
free(arguments);
}
For the command-line arguments in argv you don't really need to create a new array to hold them, but it showcases how to create an array of string to pass to strmaker. You can use any strings you want instead of the command-line arguments.
Why doesn't this code work? What I have been trying to do is make a dynamic allocation for an unknown user input length of an array using int main(int ac, char ** ac) and malloc().
#include <stdio.h>
#include <stdlib.h>
int main(int ac, char **av)
{
char *str;
int i;
str = malloc(sizeof(char) * (ac + 1));
i = 0;
if(str[i] != '\0')
{
i = i + 1;
printf("%s\n\t", str);
}
return(0);
}
Name the parameters of main() in the standardized way: int argc, char *argv[].
(note that argv[0] is the name of the executable, which may or may not be of interest to you)
First you need to allocate argc number of pointers to character. Optionally with a NULL sentinel value at the end if that makes sense for your program - don't confuse this with null termination though.
So you should rather have something like char** str_table = malloc(sizeof(*str_table) * argc);
For each item in str_table, allocate additional memory for strlen(argv[i]) + 1 characters, the actual data. In this case the +1 is for null termination and it is mandatory.
strcpy from argv[i] to your own array.
ac is not the length of any argument, but rather the argument count.
When the user specifies one argument this will always be 2.
If the program should only output the first argument you can just do:
#include <stdio.h>
int main(int argc, char **argv) {
if(argc == 2)
printf("%s\n", argv[1]);
return 0;
}
If you want to load the argument into a string you have to get it's length.
This can for example be done with the strlen() function:
#include <stdio.h> /* for printf */
#include <stdlib.h> /* for malloc and free */
#include <string.h> /* for strlen and strcpy */
int main(int argc, char **argv) {
if(argc == 2) {
char *input_string;
int input_string_length;
input_string_length = strlen(argv[1]);
/* strlen() does not include the '\0' in the length */
input_string = malloc((input_string_length + 1) * sizeof(char));
strcpy(input_string, argv[1]);
printf("%s\n", input_string);
free(input_string);
}
return 0;
}
ac doesn't represent user input length (as you have been trying to allocate memory for ac+1) + str points to a raw memory location without any valid data, even then if you want to go with ac then:
#include <stdio.h>
#include <stdlib.h>
#include <string.h> //added for memset
int main(int ac, char **av)
{
char *str;
int i; //USELESS
str = malloc(sizeof(char) * (ac + 1)); //str points to a memory (if allocated) which contains garbage
i = 0; //NO USE
if(str) //checks if memory has been allocated - NO INCREMENT NEEDED OR VALID
{
printf("Memory has been successfully allocated");
memset(str,'\0',ac+1); //added for NULL termination
}
return(0);
}
In case you are trying to allocate memory on the stack, you may also look for VLA.
This statement
str = malloc(sizeof(char) * (ac + 1));
does not make sense.
Moreover the allocated array was not initialized. So this statement
if(str[i] != '\0')
results in undefined behavior.
It seems what you are trying to do is to output command line parameters by copying them in dynamically allocated arrays.
If so then the program can look the following way
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main( int argc, char * argv[] )
{
if ( argc > 1 )
{
char **s = malloc( ( argc - 1 ) * sizeof( char *) );
for ( int i = 0; i < argc - 1; i++ )
{
s[i] = malloc( strlen( argv[i+1] ) + 1 );
strcpy( s[i], argv[i+1] );
}
for ( int i = 0; i < argc - 1; i++ )
{
puts( s[i] );
}
for ( int i = 0; i < argc - 1; i++ ) free( s[i] );
free( s );
}
return 0;
}
If to run the program like for example
program Hello World
then the output will be
Hello
World
You can add yourself checks to the program that the memory allocations were successful.
There are some logic error in your code.
int ac is the number of arguments.
char **av contain the arguments.
For example : ./program arg1 arg2
av[0] is gonna be "./program"
av[1] is gonna be "arg1"
av[2] is gonna be "arg2"
av[2][0] is gonna be "a"
Now if you just want the size of the first argument you can use strlen() :
int size = strlen(av[1]);
What you need instead of your if statement is a while loop to go throught all the elements of av.
For example :
int i = 0;
while (i <= ac) {
printf("%s", av[i]);
...Your code...
i++;
}
I've written the code, however I cannot remember how to get the user's input.
The command to run the code is ./rle "HHEELLLLO W" but in my code, I don't know how to get the code to read the "HHEELLLLO W".
If I have a regular printf function, the code will work and will print H1E1L3O 0W0 but I want it so any user's input can be calculated.
I tried using atoi but I think I've done it wrong and I don't know how to fix it.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define MAX_RLEN 50
char* encode(char* src)
{
int rLen;
char count[MAX_RLEN];
int len = strlen(src);
char* dest = (char*)malloc(sizeof(char) * (len * 2 + 1));
int i, j = 0, k;
for (i = 0; i < len; i++) {
dest[j++] = src[i];
rLen = 1;
while (i + 1 < len && src[i] == src[i + 1]) {
rLen++;
i++;
}
sprintf(count, "%d", rLen);
for (k = 0; *(count + k); k++, j++) {
dest[j] = count[k];
}
}
dest[j] = '\0';
return dest;
}
int main(int argc, char ** argv)
{
char str[] = atoi(argv[1]);
char* res = encode(str);
printf("%s", res);
getchar();
}
When I compile it, I get this error:
rle.c: In function ‘main’:
rle.c:47:18: error: invalid initializer
char str[] = atoi(argv[1]);
^~~~
rle.c:45:14: error: unused parameter ‘argc’ [-Werror=unused-parameter]
int main(int argc, char ** argv)
^~~~
cc1: all warnings being treated as errors
atoi converts strings (of digits) to integers, which is nothing like what you to do here.
I would recommend doing this one of two ways:
(1) Use the command-line argument directly:
int main(int argc, char ** argv)
{
char* str = argv[1];
char* res = encode(str);
printf("%s\n", res);
}
This works because argv[1] is already a string, as you want. (In fact in this case you don't even need str; you could just do char* res = encode(argv[1]);.)
In this case you will have the issue that the shell will break the command line up into words as argv[1], argv[2], etc., so argv[1] will contain just the first word. You can either use quotes on the command line to force everything into argv[1], or use the next technique.
(2) Read a line from the user:
int main(int argc, char ** argv)
{
char str[100];
printf("type a string:\n");
fgets(str, sizeof(str), stdin);
char* res = encode(str);
printf("%s\n", res);
}
In both cases there's also some additional error checking you theoretically ought to do.
In the first case you're assuming the user actually gave you a command-line argument. If the user runs your program without providing an argument, argv[1] will be a null pointer, and your code will probably crash. To prevent that, you could add the test
if(argc <= 1) {
printf("You didn't type anything!\n");
exit(1);
}
At the same time you could double-check that there aren't any extra arguments:
if(argc > 2) {
printf("(warning: extra argument(s) ignored)\n");
}
In the second case, where you prompt the user, there's still the chance that they won't type anything, so you should check the return value from fgets:
if(fgets(str, sizeof(str), stdin) == NULL) {
printf("You didn't type anything!\n");
exit(1);
}
As you'll notice if you try this, there's also the issue that fgets leaves the \n in the buffer, which may not be what you want.
I am a beginner in coding and having difficulty trying to take input from both command line as well as console(STDIN). my program is supposed to search and replace a group of characters from a string. For example, concatenate cat gat : the output must be congatenate!
This is my code so far!
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
/*Function to replace a string with another string*/
char *rep_str(const char *s, const char *old, const char *new1)
{
char *ret;
int i, count = 0;
int newlen = strlen(new1);
int oldlen = strlen(old);
for (i = 0; s[i] != '\0'; i++)
{
if (strstr(&s[i], old) == &s[i])
{
count++;
i += oldlen - 1;
}
}
ret = (char *)malloc(i + count * (newlen - oldlen));
if (ret == NULL)
exit(EXIT_FAILURE);
i = 0;
while (*s)
{
if (strstr(s, old) == s) //compare the substring with the newstring
{
strcpy(&ret[i], new1);
i += newlen; //adding newlength to the new string
s += oldlen;//adding the same old length the old string
}
else
ret[i++] = *s++;
}
ret[i] = '\0';
return ret;
}
int main(int argc, char*agrv[])
{
char mystr[100], c[10], d[10];
scanf("%s", mystr);
scanf(" %s",c);
scanf(" %s",d);
char *newstr = NULL;
newstr = rep_str(mystr, c,d);
printf("%s\n", newstr);
free(newstr);
return 0;
}
as for now, it shows correct output for either console input or commandline input, bur not both!
kindly suggest the changes to be made!
You can have a check on the variable argc of function int main().
// Path of executable file is passed by default so value of 'argc' will be at least 1
if(argc > 1)
{
// do stuff to work on input from command line
}else{
// do stuff to work on input from STDIN
}
Instead of trying to parse input file through argc and argv, simply pass all the input file through stdin. This means that you will always use scanf to read input data.
At command line you will need to call using pipes, something like this:
$ cat file.txt | yourprogram
I am trying to convert some code that is meant to remove all non-numeric characters except for "_" from a command line argument, except instead of a command line argument I am trying to get the code to accept input from a regular string, I've tried to convert the code to accept strings, but i keep getting this error
words.c:9: warning: assignment makes pointer from integer without a cast
I am confused as to what I am doing wrong so I would really appreciate any help that I can get with this problem, thanks!
Also here is the original code that accepts command line arguments
#include <stdio.h>
#include <string.h>
#include <ctype.h>
int main(int argc, char ** argv) {
int i;
char *p;
if (argc > 1) {
for (p = argv[1]; *p != '\0'; p++) {
if (islower(*p) || isdigit(*p) || *p == '_') {
putchar (*p);
}
}
putchar ('\n');
}
return 0;
}
and here is my "version"
#include <stdio.h>
#include <string.h>
#include <ctype.h>
int main(void) {
int i;
char *p;
char stg[] = "hello";
// if (argc > 1) {
for (p = stg[1]; *p != '\0'; p++) {
if (isalnum(*p) || *p == '_') {
putchar (*p);
}
}
putchar ('\n');
return 0;
}
In your code p is a pointer. Change p = stg[1]; to p = &stg[1];.