Breaking a string pointer till a marker in c - c

I have a pointer char * c = "I - need - to - do - this - break". I need to break it according to the '-' such that in each iteration I get an output as "I" then "I -need" then " I - need - to" and so on till the whole string.
Any help?

The simple way is to make c mutable and not a pointer to a String Literal. That way all you need to do is work down the string (either using a pointer or an index) and keep track of whether you are in a word or not in a word. When you hit a space (or hyphen if you get rid of the spaces), if you are in a word, save the current char, overwrite the current char in the array with '\0' to terminate the string at the current character and print it. Restore the current character in the array and repeat until you run out of characters, e.g.
#include <stdio.h>
int main (void) {
char c[] = "I - need - to - do - this - break", /* an array */
*p = c; /* pointer to current char */
int in = 0; /* flag for in/out of word */
while (*p) { /* loop over each char */
if (*p == ' ' || *p == '-') { /* if a space or hyphen */
if (in) { /* if in a word */
char current = *p; /* save current char */
*p = 0; /* nul-terminate array at current */
puts (c); /* print array */
*p = current; /* restore current in array */
in = 0; /* set flag out of word */
}
}
else { /* otherwise, not a space or hyphen */
in = 1; /* set flag in word */
}
p++; /* advance to next char */
}
if (in) /* if in word when last char reached */
puts (c); /* output full string */
}
Example Use/Output
$ ./bin/incremental
I
I - need
I - need - to
I - need - to - do
I - need - to - do - this
I - need - to - do - this - break
Using a Non-Mutable String Literal
If you must use a non-mutable String Literal, then the approach is largely the same. The only difference being you cannot nul-terminate the original string, so you are left using another pointer (or index) to output each character from the beginning until you reach the current using putchar (or getting the number of characters from p - c and then copying to a buffer to nul-terminate and output all at once). Simply looping until you reach the current and using putchar for output is about as easy as anything else, e.g.
#include <stdio.h>
int main (void) {
char *c = "I - need - to - do - this - break", /* string literal */
*p = c; /* pointer to current char */
int in = 0; /* flag for in/out of word */
while (*p) { /* loop over each char */
if (*p == ' ' || *p == '-') { /* if a space or hypen */
if (in) { /* if in a word */
char *wp = c; /* get pointer to start */
while (wp < p) /* loop until you reach current */
putchar (*wp++); /* output each char & increment */
putchar ('\n'); /* tidy up with newline */
in = 0; /* set flag out of word */
}
}
else { /* otherwise, not a space or hyphen */
in = 1; /* set flag in word */
}
p++; /* advance to next char */
}
if (in) /* if in word when last char reached */
puts (c); /* output full string */
}
(the output is the same)
Look things over and let me know if you have questions.

Related

How to swap array of characters from second array to last second array

Swap 2 first character to second last character and so on like 3 first character to third last character
untill character to middle of the word.
Input:
I love programming
Output:
i lvoe pnimmargorg
I tried to do
#include <stdio.h>
#include <string.h>
int main(){
int t;
scanf("%d",&t);
char s[105+t];
getchar();
for(int i=0;i<t;i++){
scanf("%[^\n]",s);
int len = strlen(s);
char temp[105+t];
getchar();
for(int i=0;i<len/2;i++){
strcpy(temp,s);
s[i] = s[len-i-1];
s[len-i-1] = temp;
}
for(int i=0;i<len/2;i++){
printf("%c",s[i]);
}
}
return 0;
}
This should do for swapping
for(int i=0;i<len/2;i++)
{
temp = s[i];
s[i] = s[len-i-1];
s[len-i-1] = temp;
}
There are a number of ways to approach this. The key is you want to be able to locate the beginning and end of each word in a line. (actually the 2nd and next to last character in each word). You cannot use strtok() or strsep() as both write nul-terminating characters in place of delimiters in your string. What you need is some way to manually advance though your line to locate the beginning and ending of each word.
One way is to use a pair of pointers and nested loops, with the outer loop advancing the start-pointer until the beginning of the next word is found. Then setting the end-pointer equal to the start-pointer and looping with a nested loop moving the end pointer until the next space or end-of-line is found. You know what lies between the two pointers will be your word.
Advance the start-pointer by one to the 2nd character and decrement the end-pointer by two to locate the next to last character (you subtract 2, one because you currently point to the next space after the word -- to find the last char, and then subtract again for the next-to-last char)
Another option is to use strspn() and strcspn() to take the place of the nested loops. strspn() returns the number of left-most characters in the string made up of only characters in the accept parameter (use " \t\n" to cause it to skip over spaces to the start of the next word) and strcspn() which returns the left-most number of character not including the characters in the reject parameter. (use the same " \t\n" to cause it to skip over all characters in the work to the next space returning the length of the word).
Writing a simple function to apply the logic to each word in the given string and swapping from the 2nd to next-to-last character in each word could be done as:
char *outsidein (char *s)
{
char *p = s; /* pointer to position in s to work toward end */
while (1) {
size_t offset = strspn (p, " \t\n"), /* find 1st non-space */
length = strcspn (p + offset, " \t\n"); /* find next space */
if (!length) /* at end, break */
break;
char *sp = p + offset + 1, /* start pointer to 2nd char in word */
*ep = p + offset + length - 2; /* end pointer to next to last char */
while (sp < ep) { /* loop toward middle */
int tmp = *ep; /* swap chars at sp & ep */
*ep-- = *sp;
*sp++ = tmp;
}
p += offset + length; /* update pointer to space following word */
}
return s; /* for convenience, return a pointer to s for immediate use */
}
(note: the string passed as the parameter s must be a mutable string)
Above the pointer p is advanced down the string saving the beginning location of each word, and then the number of spaces (offset) and length is added to it to advance to the next space after the word you processed.
A short example program that puts it altogether could be:
#include <stdio.h>
#include <string.h>
#define MAXC 1024 /* if you need a constant, #define one (or more) */
char *outsidein (char *s)
{
char *p = s; /* pointer to position in s to work toward end */
while (1) {
size_t offset = strspn (p, " \t\n"), /* find 1st non-space */
length = strcspn (p + offset, " \t\n"); /* find next space */
if (!length) /* at end, break */
break;
char *sp = p + offset + 1, /* start pointer to 2nd char in word */
*ep = p + offset + length - 2; /* end pointer to next to last char */
while (sp < ep) { /* loop toward middle */
int tmp = *ep; /* swap chars at sp & ep */
*ep-- = *sp;
*sp++ = tmp;
}
p += offset + length; /* update pointer to space following word */
}
return s; /* for convenience, return a pointer to s for immediate use */
}
int main (void) {
char line[MAXC];
while (fgets (line, MAXC, stdin)) { /* read each line */
line[strcspn (line, "\n")] = 0; /* trim \n character from end */
printf ("%s\n", outsidein (line)); /* output modified line */
}
}
Example Use/Output
$ echo "I love programming" | ./bin/str_outsidein
I lvoe pnimmargorg
There are a number of ways to do it. Look things over and let me know if you have questions.
That's a nice little exercise, that seems tricky at frst, but keeping it simple keeps the bugs away.
Things to look out for (aka traps for young players)
multiple spaces between words.
input strings that start with spaces.
input strings that end with a space.
See comments in code below.
#include <stdio.h>
#include <string.h>
void scramble(char* str)
{
char *p, *q, *word_start;
char c;
if (str == NULL)
return;
while (*str != 0)
{
// skip spaces, you could also use while (isspace(*str & 0xFF))
// which would suddenly make this function much more
// versatile and interesting.
while (*str == ' ')
++str;
// keep track of the start of the word
word_start = str;
// find end of word, you could also use isspace() here,
// if you decide to go that route.
while (*str != ' ' && *str != 0)
++str;
// str now points to one character past the last char of word
// swap the letters we want, from second to second to last,
// leave the others untouched. Note that the p < q test
// automatically skips processing of words of 3 bytes or less.
// so this takes also care of strings ending with a space
// which ould give us an empty word (when str == word_start)
for (p = word_start + 1, q = str - 2; p < q; ++p, --q)
{
c = *p;
*p = *q;
*q = c;
}
}
}
const char input_string[] = "I love programming";
int main()
{
char output_string[sizeof(input_string)];
strcpy(output_string, input_string);
printf("input: %s\n", input_string);
scramble(output_string);
printf("output: %s\n", output_string);
return 0;
}
Try it, and step through it: https://onlinegdb.com/SkxKerFYP

How can I parse the command line with semicolons?

I am trying to read the command line arguments that are separated by a semicolon with a blank space front and back (such as ls ; date ; cal), but the separation part has been difficult. My codes are working when I simply put an individual command line (such as ls or date), but whenever I put the semicolon, it does not work (such as ls ; date)
Here is my C code:
void parse(char *userInput, char **splitInput)
{
//read until userInput is not end of line
while (*userInput != '\0')
{
//replace any space in userInput as '\0'
while (*userInput == ';')
{
*userInput++ = '\0';
}
//save the argument position
*splitInput++ = userInput;
//if userinput is not equal to space, read next userInput
while (*userInput != ' ' && *userInput != ';' && *userInput != '\0')
{
userInput++;
}
}
}
void execute(char **splitInput)
{
pid_t pid = fork();
if (pid > 0) //parent process
{
pid_t parent_pid;
parent_pid = wait(NULL);
}
else if (pid == 0) //child process
{
if (execvp(*splitInput, splitInput) < 0)
{
printf("%s: command not found\n", *splitInput);
exit(1);
}
}
else //error
{
perror("fort error");
}
}
void main(void)
{
char userInput[100]; //execvp's first argument
char *splitInput[100]; //execvp's second argument
while(strcmp(userInput,"quit") != 0)
{
//ask for a user input
printf("group 10> ");
//read the entire line of input
scanf("%[^\n]", userInput);
//get characters from input; stop the loop problem
getchar();
//quit if user input is equal to quit
if (strcmp(userInput, "quit") == 0)
{
exit(0);
}
//parse the input
parse(userInput, splitInput);
//execute fork
execute(splitInput);
}
}
There are a number of ways to do this. string.h provides several functions that can be used, strtok(), strsep(), strchr(), or a combination of strcspn() and strspn() depending on your needs. You can also always walk-a-pointer down the string picking out the wanted tokens from the string and ignoring whitespace and multiple-included-delimiters. There is actually good pointer learning value in approaching it this way from an education standpoint.
Any time you are looping over anything picking out various pieces, instead of trying to include multiple nested while loops, each designed to scan-forward to either skip or find a certain class of characters, it is often more advantageous to use a state-loop where you use one of more flags to keep track of differing states. (line in-word reading character, or between words reading delimiters or whitespace, etc..). That way no nested loops are required and you simply use a single loop to loop over each character responding accordingly depending on your current state.
Putting that in work to scan down your string and pick out each of the words terminated by a delimiter ';' or by whitespace, and keeping a single state flag int in; to track whether you are in-word reading characters (in = 1;) or between words handling spaces and delimiters (in = 0;) and use char *sp as the start-pointer pointing to the beginning of each word and userInput as the end-pointer pointing to the current character being read, you could do:
void parse(char *userInput, char **splitInput, char delim, size_t nptrs)
{
int in = 0; /* simple in-word flag 0-false/1-true */
size_t n = 0; /* counter to protect splitInput bounds */
char *sp = userInput; /* start-pointer initialized to userInput */
while (n < nptrs - 1) { /* loop while pointers remain unfilled */
/* if at end, is whitespace or a delimiter */
if (!*userInput || isspace(*userInput) || *userInput == delim) {
if (in) { /* if in word */
splitInput[n++] = sp; /* set pointer to start-pointer */
splitInput[n] = NULL; /* set next pointer NULL */
}
in = 0; /* reset in flag zero */
if (*userInput) /* if space or delim, nul-terminate */
*userInput = 0;
else /* otherwise */
return; /* at end-of-string */
}
else { /* normal char */
if (!in) { /* if not in-word */
sp = userInput; /* set start-pointer to 1st good char */
in = 1; /* set in-word flag true */
}
}
userInput++; /* advance to next char */
}
}
(note: above the delim character is passed as a parameter along with nptrs to pass the number of pointers you have available so you can protect your pointer array bounds while filling pointers. Also note, the function always sets the next pointer in your array to NULL as a sentinel allowing you to loop over the pointers in your array in main() until NULL is reached since you don't return the number of pointers used, either as the function return or through a pointer parameter)
A simple example that parses the words from " my; ; ; dog ;;; has;fleas ;" using ';' or whitespace as delimiters could be:
#include <stdio.h>
#include <ctype.h>
#define NPTR 32 /* if you need a constant, #define one (or more) */
void parse(char *userInput, char **splitInput, char delim, size_t nptrs)
{
int in = 0; /* simple in-word flag 0-false/1-true */
size_t n = 0; /* counter to protect splitInput bounds */
char *sp = userInput; /* start-pointer initialized to userInput */
while (n < nptrs - 1) { /* loop while pointers remain unfilled */
/* if at end, is whitespace or a delimiter */
if (!*userInput || isspace(*userInput) || *userInput == delim) {
if (in) { /* if in word */
splitInput[n++] = sp; /* set pointer to start-pointer */
splitInput[n] = NULL; /* set next pointer NULL */
}
in = 0; /* reset in flag zero */
if (*userInput) /* if space or delim, nul-terminate */
*userInput = 0;
else /* otherwise */
return; /* at end-of-string */
}
else { /* normal char */
if (!in) { /* if not in-word */
sp = userInput; /* set start-pointer to 1st good char */
in = 1; /* set in-word flag true */
}
}
userInput++; /* advance to next char */
}
}
int main (void) {
char s[] = " my; ; ; dog ;;; has;fleas ;", *split[NPTR] = { NULL }, **p = split;
parse (s, split, ';', NPTR);
while (*p)
printf ("'%s'\n", *p++);
}
(note: the header ctype.h is included to make use of the isspace() function to test for whitespace rather than stringing together if() statements checking for space, '\t' or '\n'directly. This is generally good practice.)
Example Use/Output
$ ./bin/split_ptr_arr3
'my'
'dog'
'has'
'fleas'
Note in the output above all included whitespace is removed.
Look things over and let me know if you have questions. There are literally dozens of way to approach splitting strings, this is just one common and basic approach.

A conditional problem when changing each characters in a string

Problem: take a string and move every character in the alphabet 13 times forward; for example 'hello' would be 'uryyb', but the trick here is that if there is a vowel in the element before then its 14 spaces so it would be 'urzyb'. I got the 14 space but then nothing else happens to the other letters so I keep getting 'hezlo', but if remove the // and use this line of code
message[i] = message[i] + key;`
then it doesn't do the 14 and only does 13 times. Can someone explain what I'm doing wrong and how to fix it?
#include<stdio.h>
int main()
{
char message[100], ch;
int i, key;
printf("Enter a message to encrypt: ");
gets(message);
printf("Enter key: ");
scanf("%d", &key);
for(i = 0; message[i] != '\0'; ++i){
if(message[i] >= 'a' && message[i-1] <= 'z'){
if(message[i-1] == 'e'){
message[i]=message[i] + 14;
}
//message[i] = message[i] + key;
if(message[i] > 'z'){
message[i] = message[i] - 'z' + 'a'-1 ;
}
message[i] = message[i];
}
}
printf("Encrypted message: %s", message);
return 0;
}
Output is hezlo
should be urzyb
I have three advises for you.
Don't use gets, it is deprecated and for good reason, use fgets instead.
Since you are modifying the message character by character. You cannot look back at the previous character using message[i-1] to see if that was a wovel, because it was already shifted in the previous iteration of the loop. store the previous character in a separate variable instead.
Since you are wrapping back to 'a' when you reach 'z', consider using the modulus arithmetic, which is used to cycle the numbers in a given range.
see the code below with these ideas applied to your code.
int main()
{
// ...
printf("Enter a message to encrypt: ");
fgets(message,100,stdin);
printf("Enter key: ");
scanf("%d", &key);
char p = 1; // some initial value that is not a vowel.
for(i = 0; message[i] != '\0'; ++i){
if(message[i] >= 'a' && message[i] <= 'z'){
char ch = (message[i]-'a' + key) % 26 + 'a'; // using modular math
if (strchr("aeiou",p)){
ch++; // increment by 1 if previous character was a vowel
}
p = message[i]; // save previous character
message[i]=ch; // update message.
}
}
printf("Encrypted message: %s", message);
return 0;
}
First of all, pay attention to things like this one:
if(message[i-1] == ...
because on the first iteration the index i is 0: so, i-1 is negative, and does not address a valid character of the string. This is an error. The way to solve this is that if the index (i) is 0, then there is no previous character, so you know it can not be a vowel.
Second, you state that you want to "slide" the characters: then you must include a statement to do that, like this:
message[i] = message[i] + key;
Try to describe the algorithm in plain english, then translate it in C code. Something like this:
Scan all characters, and add 13 to each; but if the preceding char is a vowel, than add 14 instead of 13.
The direct outcome of the previous sentence goes like this:
for(i = 0; message[i] != '\0'; i++) {
if (i > 0) {
// we are past the first letter
if (message[i-1]=='a' || message[i-1]=='e' || ...)
message[i] += 14;
else message[i] += 13;
} else {
// it's the first letter, there can not be a preceding vowel
message[i] += 13;
}
}
The above algorithm has some problem - mainly, it does tests for vowels on the already modified message. And it is slightly too verbose. Better to do something like this (warning, untested):
int wasvowel = 0;
char previous;
for(i = 0; message[i] != '\0'; i++) {
previous = message[i]; // save for later
if (wasvowel)
message[i] += key+1;
else message[i] += key;
wasvowel = previous=='a' || previous=='e' ...
}
The code above misses some checks; it is not clear what to do if the translated char becomes a not-letter, for example. But the general idea is there.
A note about the variable "previous" (perhaps the name is not very correct). The algorithm must consider each char in order to determine whether it is a vowel or not. But this check must be made before changing the string. Imagine what happens with words having two vowels in a row, like "aerial". The letter 'e' must be slided 14 times, ok, but the letter 'r' too. So we must remember that the letter before the 'r' was a vowel, and to do that we must preserve the 'e'. There are other methods to do that, but this one is simple.
Another note about the variable wasvowel. Prior to the cycle its value is set to 0: of course there is no vowel before the start of the message. Then in the cycle wasvowel is set, ready for the next iteration. In the last iteration, wasvowel is set again, but the value will be never used. This is, I think, acceptable in this context - it is possible that, in another context, it would not.
Because you are overwriting the main string, the e becomes r and then you can't read e to compare to anymore. That's why you are getting uryyb instead of urzyb. This is a modified code with an alternative vowel check method and another buffer for the modified string, keeping the original intact:
#include <stdio.h>
#include <string.h>
int main()
{
unsigned char message[100], original[100], ch;
int i, key, len;
memset(message, 0, 100);
memset(original, 0, 100);
printf("Enter a message to encrypt: ");
scanf("%s", original);
printf("Enter key: ");
scanf("%d", &key);
for(i = 0, len = strlen(original); i < len; i++){
if(original[i] >= 'a' && original[i] <= 'z'){
if(i > 0 && memchr("aeiou", original[i-1], 5)){
message[i] = original[i] + 14;
}else{
message[i] = original[i] + 13;
}
}
}
for(i = 0, len = strlen(message); i < len; i++){
if(message[i] > 'z'){
message[i] = message[i] - 'z' + 'a' - 1;
}
}
printf("Encrypted message: %s", message);
return 0;
}
Edit: moved the overflow fix outside another loop.
Edit: forgot an crucial part, chars need to be unsigned.
https://onlinegdb.com/ryo2dKR5H
First, don't use gets() it is so vulnerable to exploit by buffer overrun is has been completely removed from the current C standard library, see Why gets() is so dangerous it should never be used!
While there is nothing wrong with reading and buffering your input (using fgets() or POSIX getline()), there is no real need to do so if you simply want to output the encrypted input. You can use getchar() to read each character of input and simply convert each character as they are read.
Regardless whether you buffer the input of just convert it on the fly, organizing your +13 (or if previous char was a vowel +14) logic into a simple function that takes the current character c and the previous character prev can help keep your logic straight and code clean, e.g.
/* simple function to encrypt lowercase chars,
* prev == vowel, c + 14, else c + 13
*/
char encrypt (const char c, const char prev)
{
char enc;
if (!islower (c)) /* validate c is lowercase */
return c;
if (prev == 'a' || prev == 'e' || prev == 'i' || /* if prev is vowel */
prev == 'o' || prev == 'u')
enc = 'a' + (c - 'a' + 14) % 26; /* add 14 to c */
else
enc = 'a' + (c - 'a' + 13) % 26; /* add 13 to c */
return enc; /* return encrypted char */
}
(note: the validation of the input with islower() from the ctype.h header ensures your conversion is applied to only lowercase characters. You can expand it to handle both cases -- that is left to you)
Also note the logic of the addition wraps back to the beginning in case adding +13 (or +14) would result in a character beyond 'z'. (for example 'z' + 13 == 'm'). You can adjust as required.
Then your code becomes simply:
int main (void) {
int c, prev = 0;
fputs ("Enter a message to encrypt: ", stdout); /* prompt */
while ((c = getchar()) != '\n' && c != EOF) { /* read each char */
putchar (encrypt (c, prev)); /* output encrypted */
prev = c; /* save current as prev */
}
putchar ('\n'); /* tidy up with newline */
}
Putting it altogether in a short example and adding the two required header files, you could do:
#include <stdio.h>
#include <ctype.h>
/* simple function to encrypt lowercase chars,
* prev == vowel, c + 14, else c + 13
*/
char encrypt (const char c, const char prev)
{
char enc;
if (!islower (c)) /* validate c is lowercase */
return c;
if (prev == 'a' || prev == 'e' || prev == 'i' || /* if prev is vowel */
prev == 'o' || prev == 'u')
enc = 'a' + (c - 'a' + 14) % 26; /* add 14 to c */
else
enc = 'a' + (c - 'a' + 13) % 26; /* add 13 to c */
return enc; /* return encrypted char */
}
int main (void) {
int c, prev = 0;
fputs ("Enter a message to encrypt: ", stdout); /* prompt */
while ((c = getchar()) != '\n' && c != EOF) { /* read each char */
putchar (encrypt (c, prev)); /* output encrypted */
prev = c; /* save current as prev */
}
putchar ('\n'); /* tidy up with newline */
}
Example Use/Output
$ ./bin/charencrypt
Enter a message to encrypt: hello
urzyb
Look things over and let me know if you have further questions.
Edit - Converting an Array of Chars
To read into a buffer (array) of char and then convert each character in the buffer based on the same logic, requires little change. The only change required is instead of converting each character on-the-fly, you read your input into a buffer, then loop over each character in the buffer making the conversions. (only caveat, you must save the prev/last char before making the conversion so you know how to correctly apply the next conversion)
The changes needed to the above are minimal. The follow uses the exact same encrypt function and a single character array to store what is read from the user and then the conversions are made in-place updating the characters in the same array, e.g.
#include <stdio.h>
#include <ctype.h>
#define MAXC 2048 /* don't skimp on buffer size (except for embedded dev.) */
/* simple function to encrypt lowercase chars,
* prev == vowel, c + 14, else c + 13
*/
char encrypt (const char c, const char prev)
{
char enc;
if (!islower (c)) /* validate c is lowercase */
return c;
if (prev == 'a' || prev == 'e' || prev == 'i' || /* if prev is vowel */
prev == 'o' || prev == 'u')
enc = 'a' + (c - 'a' + 14) % 26; /* add 14 to c */
else
enc = 'a' + (c - 'a' + 13) % 26; /* add 13 to c */
return enc; /* return encrypted char */
}
int main (void) {
char buf[MAXC], *p = buf; /* buffer and pointer to buffer */
int current, prev = 0;
fputs ("Enter a message to encrypt: ", stdout); /* prompt */
if (!fgets (buf, MAXC, stdin)) { /* read into buffer */
fputs ("(user canceled input)\n", stdout);
return 1;
}
while (*p && *p != '\n') { /* read each char */
current = *p; /* save current */
*p = (encrypt (*p, prev)); /* encrypt char */
prev = current; /* set prev to current */
p++; /* advance to next char */
}
fputs (buf, stdout); /* output converted buffer */
}
How you loop over the characters in the array is up to you. You can use a for loop with array indexes, or simply use a pointer and advance the pointer to the next character on each iteration until you reach the nul-terminating character or '\n' character, each of which would signify the end of the characters you are converting.
If you want to use a second array so that you preserve both the original and have the new encrypted array, just declare another array and fill it in the same manner, except instead of writing the converted values back to the original array, write it to your second array (don't forget to nul-terminate the second array)
Example Use/Output
The out is exactly the same:
$ ./bin/charencryptbuf
Enter a message to encrypt: hello
urzyb
Let me know if you have further questions.
Edit Based On Comment To Encrypt Multiple-Words
If you simply want to prompt the user for the number of words to encrypt, that as in my comment, you just need to wrap what you are doing in a loop. Prompt the user for the number of words to encrypt, then loop that number of times encrypting each word. The changes to the code above are minimal, e.g.
int main (void) {
char buf[MAXC]; /* buffer to hold input */
int nwords; /* no. of words to encrypt */
fputs ("How may words to encrypt?: ", stdout); /* prompt no. of words */
if (!fgets (buf, MAXC, stdin) || sscanf (buf, "%d", &nwords) != 1 ||
nwords < 1) {
fputs ("error: invalid integer input or nothing to do.\n", stderr);
return 1;
}
for (int i = 0; i < nwords; i++) {
char *p = buf; /* pointer to buf */
int current, prev = 0; /* current and prev chars */
printf ("\nenter word[%d]: ", i + 1); /* prompt */
if (!fgets (buf, MAXC, stdin)) { /* read into buffer */
fputs ("(user canceled input)\n", stdout);
return 1;
}
while (*p && *p != '\n') { /* read each char */
current = *p; /* save current */
*p = (encrypt (*p, prev)); /* encrypt char */
prev = current; /* set prev to current */
p++; /* advance to next char */
}
fputs (buf, stdout); /* output converted buffer */
}
}
Example Use/Output
$ ./bin/charencryptbufmulti
How may words to encrypt?: 6
enter word[1]: hello
urzyb
enter word[2]: there
gurfr
enter word[3]: how
ubk
enter word[4]: are
nfr
enter word[5]: you
lbi
enter word[6]: hello
urzyb
Separating And Encrypting Any Number of Words Entered by the User Individually
To encrypt multiple words separately, you just need to do what you are doing for the whole string but separating the input into tokens (individual words). C provides the strtok() function to do just that, but you will want to make a copy of the entire input string if you need to preserve it as strtok() modifies the original. You can also simply make a copy of each token to preserve the original word and encrypted word separately.
An easy way to implement the addition is just to write a small wrapper-function that takes whole words as an input parameter and then passes the word to the existing encrypt() function. So for the encrypt-word (or encrypt-wrapper), you could do:
/** Simple wrapper function that takes a word and passes it to encrypt */
char *encryptw (char *buf)
{
char *p = buf; /* pointer to buffer holding word */
int current, prev = 0; /* current and previous characters */
while (*p) { /* read each char */
current = *p; /* save current */
*p = (encrypt (*p, prev)); /* encrypt char */
prev = current; /* set prev to current */
p++; /* advance to next char */
}
return buf;
}
(note: the char* type and return buf; is just a convenience to allow you make immediate use of the encrypted word, e.g. char word[] = "hello"; puts (encryptw (word)); Also note, encryptw() modifies the input, so you could not pass a string-literal, e.g. encryptw("hello");)
Having moved the code that encrypts a word into the encryptw() function, all you need to do in main() is separate the words into tokens and pass each token to encryptw() to encrypt and then output the results. You must include string.h for strtok() as well as for strcpy(), e.g.
#include <string.h>
...
#define MAXW 128 /* max individual word size to encrypt */
#define DELIM " \t\n" /* strtok delimiters */
...
int main (void) {
...
/* loop separating buf into individual words to encrypt */
p = strtok (buf, DELIM); /* 1st call - pass buf */
while (p) { /* validate return not NULL */
strcpy (word, p); /* make copy, to preserve original */
encryptw (word); /* pass word to encryptw to encrypt word */
/* output word, original and encrypted */
printf ("word[%2zu] : %-12s : (%s)\n", ++n, p, word);
p = strtok (NULL, DELIM); /* all subsequent calls - pass NULL */
}
}
(note: above the output is now the word number encrypted, e.g. word[1].. followed by the original word and then the encrypted word in parenthesis)
The full code containing all changes would be:
#include <stdio.h>
#include <string.h>
#include <ctype.h>
#define MAXC 2048 /* don't skimp on buffer size (except for embedded dev.) */
#define MAXW 128 /* max individual word size to encrypt */
#define DELIM " \t\n" /* strtok delimiters */
/* simple function to encrypt lowercase chars,
* prev == vowel, c + 14, else c + 13
*/
char encrypt (const char c, const char prev)
{
char enc;
if (!islower (c)) /* validate c is lowercase */
return c;
if (prev == 'a' || prev == 'e' || prev == 'i' || /* if prev is vowel */
prev == 'o' || prev == 'u')
enc = 'a' + (c - 'a' + 14) % 26; /* add 14 to c */
else
enc = 'a' + (c - 'a' + 13) % 26; /* add 13 to c */
return enc; /* return encrypted char */
}
/** Simple wrapper function that takes a word and passes it to encrypt */
char *encryptw (char *buf)
{
char *p = buf; /* pointer to buffer holding word */
int current, prev = 0; /* current and previous characters */
while (*p) { /* read each char */
current = *p; /* save current */
*p = (encrypt (*p, prev)); /* encrypt char */
prev = current; /* set prev to current */
p++; /* advance to next char */
}
return buf;
}
int main (void) {
char buf[MAXC], *p = buf, /* buffer and pointer to buffer */
word[MAXW]; /* array for word to encrypt */
size_t n = 0;
fputs ("Enter a message to encrypt: ", stdout); /* prompt */
if (!fgets (buf, MAXC, stdin)) { /* read into buffer */
fputs ("(user canceled input)\n", stdout);
return 1;
}
putchar ('\n');
/* loop separating buf into individual words to encrypt */
p = strtok (buf, DELIM); /* 1st call - pass buf */
while (p) { /* validate return not NULL */
strcpy (word, p); /* make copy, to preserve original */
encryptw (word); /* pass word to encryptw to encrypt word */
/* output word, original and encrypted */
printf ("word[%2zu] : %-12s : (%s)\n", ++n, p, word);
p = strtok (NULL, DELIM); /* all subsequent calls - pass NULL */
}
}
Example Use/Output
Combining your original string from your question "hello" with the phrase from your last comment "how many words you want to encrypt", running the program and passing the combined string would result in:
$ ./bin/charencryptbufmulti
Enter a message to encrypt: hello how many words you want to encrypt
word[ 1] : hello : (urzyb)
word[ 2] : how : (ubk)
word[ 3] : many : (znbl)
word[ 4] : words : (jbfqf)
word[ 5] : you : (lbi)
word[ 6] : want : (jnbg)
word[ 7] : to : (gb)
word[ 8] : encrypt : (rbpelcg)
Let me know if that is what you described in your last comment and let me know if you have further questions.

Printf not printing - returns NULL

beginner here. So I'm trying to write some code that take a sentence and returns the longest word. When I debugg the program everything looks correct as I'd expect including the char array. However when I come to print the output I invariably get a NULL...
I've put in the entire code because I think one of the loops must be effecting the array string pointer in some way?
#include <stdio.h>
#include <string.h>
void LongestWord(char sen1[500]) {
/*
steps:
1. char pointer. Each Byte holds array position of each space or return value Note space = 32 & return = 10.
2. Once got above asses biggest word. Biggest word stored in short int (starting position)
3. Once got biggest word start - move to sen using strncpy
*/
char sen[500];
char *ptr = sen;
int i = 0;
int space_position[500];
int j = 0;
int k = 0;
int word_size_prior_to_each_position[500];
int l = 0;
int largest = 0;
int largest_end_position = 0;
int largest_start_position =0;
memset(&sen[0], 0, 500);
memset(&space_position[0], 0, 2000);
memset(&word_size_prior_to_each_position[0], 0, 2000);
while (i < 500) { //mark out where the spaces or final return is
if ((sen1[i] == 0b00100000) ||
(sen1[i] == 0b00001010))
{
space_position[j] = i;
j = j+1;
}
i = i+1;
}
while (k < 500) {
if (k == 0) {
word_size_prior_to_each_position[k] = (space_position[k]);
}
//calculate word size at each position
if ((k > 0) && (space_position[k] != 0x00)) {
word_size_prior_to_each_position[k] = (space_position[k] - space_position[k-1]) -1;
}
k = k+1;
}
while (l < 500) { //find largest start position
if (word_size_prior_to_each_position[l] > largest) {
largest = word_size_prior_to_each_position[l];
largest_end_position = space_position[l];
largest_start_position = space_position[l-1];
}
l = l+1;
}
strncpy(ptr, sen1+largest_start_position+1, largest);
printf("%s", *ptr);
return 0;
}
int main(void) {
char stringcapture[500];
fgets(stringcapture, 499, stdin);
LongestWord(stringcapture); //this grabs input and posts into the longestword function
return 0;
}
In the function LongestWord replace
printf("%s", *ptr);
with
printf("%s\n", ptr);
*ptr denotes a single character, but you want to print a string (see %s specification), so you must use ptr instead. It makes sense to also add a line break (\n).
Also remove the
return 0;
there, because it's a void function.
Returning the longest word
To return the longest word from the function as pointer to char, you can change the function signature to
char *LongestWord(char sen1[500])
Since your pointer ptr points to a local array in LongestWord it will result in a dangling reference as soon as the function returns.
Therefore you need to do sth like:
return strdup(ptr);
Then in main you can change your code to:
char *longest_word = LongestWord(stringcapture);
printf("%s\n", longest_word);
free(longest_word);
Some more Hints
You have a declaration
int space_position[500];
There you are calling:
memset(&space_position[0], 0, 2000);
Here you are assuming that an int is 4 bytes. That assumption leads to not-portable code.
You should rather use:
memset(&space_position[0], 0, sizeof(space_position));
You can even write:
memset(space_position, 0, sizeof(space_position));
since space_position is the address of the array anyway.
Applied to your memsets, it would look like this:
memset(sen, 0, sizeof(sen));
memset(space_position, 0, sizeof(space_position));
memset(word_size_prior_to_each_position, 0, sizeof(word_size_prior_to_each_position));
Instead of using some binary numbers for space and return, you can alternatively use the probably more readable notation of ' ' and '\n', so that you could e.g. write:
if ((sen1[i] == ' ') ||
(sen1[i] == '\n'))
instead of
if ((sen1[i] == 0b00100000) ||
(sen1[i] == 0b00001010))
The variable largest_end_position is assigned but never used somewhere. So it can be removed.
The following line
strncpy(ptr, sen1 + largest_start_position + 1, largest);
would omit the first letter of the word if the first word were also the longest. It seems largest_start_position is the position of the space, but in case of the first word (largest_start_position == 0) you start to copy from index 1. This special case needs to be handled.
You have a local array in main that is not initialized.
So instead of
char stringcapture[500];
you must write
char stringcapture[500];
memset(stringcapture, 0, sizeof(stringcapture));
alternatively you could use:
char stringcapture[500] = {0};
Finally in this line:
largest_start_position = space_position[l - 1];
You access the array outside the boundaries if l==0 (space_position[-1]). So you have to write:
if (l > 0) {
largest_start_position = space_position[l - 1];
}
else {
largest_start_position = 0;
}
While Stephan has provided you with a good answer addressing the problems you were having with your implementation of your LongestWord function, you may be over-complicating what your are doing to find the longest word.
To be useful, think about what you need to know when getting the longest word from a sentence. You want to know (1) what the longest word is; and (2) how many characters does it contain? You can always call strlen again when the function returns, but why? You will have already handled that information in the function, so you might as well make that information available back in the caller.
You can write your function in a number of ways to either return the length of the longest word, or a pointer to the longest word itself, etc. If you want to return a pointer to the longest word, you can either pass an array of sufficient size as a parameter to the function for filling within the function, or you can dynamically allocate storage within the function so that the storage survives the function return (allocated storage duration verses automatic storage duration). You can also declare an array static and preserve storage that way, but that will limit you to one use of the function in any one expression. If returning a pointer to the longest word, to also make the length available back in the caller, you can pass a pointer as a parameter and update the value at that address within your function making the length available back in the calling function.
So long as you are simply looking for the longest word, the longest word in the unabridged dictionary (non-medical) is 29-characters (taking 30-characters storage total), or for medical terms the longest word is 45-character (taking 46-characters total). So it may make more sense to simply pass an array to fill with the longest word as a parameter since you already know what the max-length needed will be (an array of 64-chars will suffice -- or double that to not skimp on buffer size, your call).
Rather than using multiple arrays, a simple loop and a pair of pointers is all you need to walk down your sentence buffer bracketing the beginning and end of each word to pick out the longest one. (and the benefit there, as opposed to using a strtok, etc. is the original sentence is left unchanged allowing it to be passed as const char * allowing the compiler to further optimize the code)
A longest_word function that passes the sentence and word to fill as parameters returning the length of the longest string is fairly straight forward to do in a single loop. Loosely referred to as a State Loop, where you use a simple flag to keep track of your read state, i.e. whether you are in a word within the sentence or whether you are in whitespace before, between or after the words in the sentence. A simple In/Out state flag.
Then you simply use a pointer p to locate the beginning of each word, and an end-pointer ep to advance down the sentence to locate the end of each word, checking for the word with the max-length as you go. You can use the isspace() macro provided in ctype.h to locate the spaces between each word.
The loop itself does nothing more than loop continually while you keep track of each pointer and then check which word is the longest by the simple pointer difference ep - p when the end of each word is found. If a word is longer than the previous max, then copy that to your longest word array and update max with the new max-length.
A short implementation could be similar to:
size_t longest_word (const char *sentence, char *word)
{
const char *p = sentence, *ep = p; /* pointer & end-pointer */
size_t in = 0, max = 0; /* in-word flag & max len */
if (!sentence || !*sentence) /* if NULL or empty, set word empty */
return (*word = 0);
for (;;) { /* loop continually */
if (isspace (*ep) || !*ep) { /* check whitespace & end of string */
if (in) { /* if in-word */
size_t len = ep - p; /* get length */
if (len > max) { /* if greater than max */
memcpy (word, p, len); /* copy to word */
word[len] = 0; /* nul-terminate word */
max = len; /* update max */
}
p = ep; /* update pointer to end-pointer */
in = 0; /* zero in-word flag */
}
if (!*ep) /* if end of word, bail */
break;
}
else { /* non-space character */
if (!in) { /* if not in-word */
p = ep; /* update pointer to end-pointer */
in = 1; /* set in-word flag */
}
}
ep++; /* advance end-pointer */
}
return max; /* return max length */
}
A complete example taking the sentence to be read as user-input could be similar to:
#include <stdio.h>
#include <string.h>
#include <ctype.h>
#define MAXWRD 64 /* longest word size */
#define MAXC 2048 /* max characters in sentence */
size_t longest_word (const char *sentence, char *word)
{
const char *p = sentence, *ep = p; /* pointer & end-pointer */
size_t in = 0, max = 0; /* in-word flag & max len */
if (!sentence || !*sentence) /* if NULL or empty, set word empty */
return (*word = 0);
for (;;) { /* loop continually */
if (isspace (*ep) || !*ep) { /* check whitespace & end of string */
if (in) { /* if in-word */
size_t len = ep - p; /* get length */
if (len > max) { /* if greater than max */
memcpy (word, p, len); /* copy to word */
word[len] = 0; /* nul-terminate word */
max = len; /* update max */
}
p = ep; /* update pointer to end-pointer */
in = 0; /* zero in-word flag */
}
if (!*ep) /* if end of word, bail */
break;
}
else { /* non-space character */
if (!in) { /* if not in-word */
p = ep; /* update pointer to end-pointer */
in = 1; /* set in-word flag */
}
}
ep++; /* advance end-pointer */
}
return max; /* return max length */
}
int main (void) {
char buf[MAXC], word[MAXWRD];
size_t len;
if (!fgets (buf, MAXC, stdin)) {
fputs ("error: user canceled input.\n", stderr);
return 1;
}
len = longest_word (buf, word);
printf ("longest: %s (%zu-chars)\n", word, len);
return 0;
}
Example Use/Output
Entered string has 2-character leading whitespace as well as 2-characters trailing whitespace:
$ ./bin/longest_word
1234 123 12 123456 1234 123456789 12345678 1 1234
longest: 123456789 (9-chars)
This isn't intended to be a substitute for Stephan's answer helping with the immediate issues in your implementation, rather this is an example providing you with an alternative way to think about approaching the problem. Generally the simpler you can keep any coding task, the less error prone it will be. Look it over and let me know if you have any further questions about the approach.

sscanf doesnt seem to capture the correct parts of my strings

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.

Resources