Parsing tokens into char ** with user input fgets() - arrays

My problem is quite similar to the problem here: Trimming a trailing \0 from fgets() in C
However, the suggested solution buffer[strcspn(buffer, "\n")] = 0; as I am using char ** to store the tokens
Here's my code:
char str[1024];
fgets(str, 1024, stdin);
const char s[2] = " ";
char *token;
char ** token_arr = malloc(100 * sizeof(char*));
int pos = 0;
token = strtok(str, s);
while( token != NULL) {
token_arr[pos] = token;
printf( "%d %s\n", pos, token );
pos++;
token = strtok(NULL, s);
}
int i = 0;
while (token_arr[i]) {
printf("%d %s \n", i, token_arr[i]);
i++;
}
Input:
a b c d e
The printf in each loop is separated by a blank line, which I presume is due to the trailing \0 that is perhaps stored inside the token_arr.
How can I remove it?
Thanks a lot
ETA: What I meant it each print loop is printing an unintended extra blank line.

Contrary to gets(), fgets() keeps the newline that you typed in after your input.
Since the newline is not in your list of tokens it is kept as a part of the last token, hence the empty line.
just replace const char s[2] = " ";
with
const char s[3] = " \n";

Related

Remove trailing newline character using fgets

I am making a program to read a file and determine if a word is a palindrome. I am running into an issue where the last token has a trailing newline and won't register as a palindrome.
Here is the file input:
leVel CompUtER Science theORY radar
And this is my code:
#include<stdio.h>
#include<string.h>
void palindrome(char str[]){
int length = strlen(str);
int i = 0;
int j = length - 1;
for(i = 0; i < length; i++){
if(str[i] != str[j]){
printf("String %s is not a palindrome.\n", str);
return;
}
j--;
}
printf("String %s is a palindrome.\n", str);
return;
}
int main() {
char line1[100];
fgets(line1, 100, stdin);
printf("%s", line1);
char *token;
token = strtok(line1, " ");
while(token != NULL){
printf("%s\n", token);
palindrome(token);
token = strtok(NULL, " ");
}
Thanks for the help!
If you are using strtok, then you can use " \n" as the delimiter and the newline will be taken care of.
int main() {
char line1[100];
fgets(line1, 100, stdin);
printf("%s", line1);
const char *delim = " \n";
char *token;
token = strtok(line1, delim);
while(token != NULL){
printf("%s\n", token);
palindrome(token);
token = strtok(NULL, delim);
}
...
}
Another great method to remove the newline is to use strcspn like this:
char line[1024];
fgets(line, sizeof line, stdin);
line[strcspn(line, "\n")] = 0; // removing newline if one is found
Why not just use fgetc and stop at the newline? You could also even just find the newline character in the string and assign '\0' to it, and it will be gone.

Tokenize user input for execvp() in c

fgets(command, BUFFSIZE, stdin);
command[strlen(command)-1] = '\0';
char *p = strtok(command, " ");
char *command_tok[BUFFSIZE];
int i = 0;
while (p) {
command_tok[i++] = p;
p = strtok(NULL, " ");
}
char *commands[] = {*command_tok, NULL};
execvp(command_tok[0], commands);
I try to get user's input, tokenize it and store command and arguments in an array to pass it to execvp(). However, the arguments are never read. If the input is "ls -l", it is always executed as "ls".
This line:
// makes a two element array
char *commands[] = {*command_tok, NULL};
is wrong.
It's creating a new array consisting of just two elements, the first of which is command_tok[0]. Instead what you want is
command_tok[i] = NULL
execvp(command_tok[0], command_tok);
Also
command[strlen(command)-1] = '\0'
is nonsensical: strlen finds the length by searching for the null byte, which you then set to null again.
Here's my version:
#include <string.h>
#include <unistd.h>
#include <cstdio>
static const int BUFFSIZE=200;
int main(int argc, char* argv[]) {
char command[BUFFSIZE] = { 0 };
fgets(command, BUFFSIZE, stdin);
// remove trailing new line if present
commmand[strcspn(command, "\n")] = 0;
char *p = strtok(command, " ");
char *command_tok[BUFFSIZE];
int i = 0;
while (p) {
command_tok[i++] = p;
p = strtok(NULL, " ");
}
command_tok[i] = NULL;
execvp(command_tok[0], command_tok);
}
The one-liner for removing trailing new line came from Removing trailing newline character from fgets() input
After the -l you don't have the space, so, your option takes the '-l\n' as the argument. So you have to use the '\n' is also in the separator in the strtok function.
The below code will work as you expected.
fgets(command, BUFFSIZE, stdin);
command[strlen(command)-1] = '\0';
char *p = strtok(command, " \n");
char *command_tok[BUFFSIZE];
int i = 0;
while (p) {
command_tok[i++] = p;
p = strtok(NULL, " \n");
}
command_tok[i] = NULL;
execvp(command_tok[0], command_tok);
When you use fgets to read the input, If it is not an EOF, you have \n at last. So, using this you can choose the argument.
You provide to commands an array that consists exactly of two elements: the (first) element command_tok points to, and NULL. How should the compiler know that you mean all elements of an array command_tok, and how should it determine its size?

Scan a sentence with space into *char array in C

I'm not good at using C language. Here is my dumb question. Now I am trying to get input from users, which may have spaces. And what I need to do is to split this sentence using space as delimiter and then put each fragment into char* array. Ex:
Assuming I have char* result[10];, and the input is: Good morning John. The output should be result[0]="Good"; result[1]="morning"; result[2]="John";I have already tried scanf("%[^\n]",input); and gets(input); Yet it is still hard to deal with String in C. And also I have tried strtok, but it seems that it only replaced the space by NULL. Hence the result will be GoodNULLmorningNULLJohn. Obviously it's not what I want. Please help my dumb question. Thanks.
Edit:
This is what I don't understand when using strtok. Here is a test code.
The substr still displayed Hello there. It seems subtok only replace a null at the space position. Thus, I can't use the substr in an if statement.
int main()
{
int i=0;
char* substr;
char str[] = "Hello there";
substr = strtok(str," ");
if(substr=="Hello"){
printf("YES!!!!!!!!!!");
}
printf("%s\n",substr);
for(i=0;i<11;i++){
printf("%c", substr[i]);
}
printf("\n");
system("pause");
return 0;
}
Never use gets, is deprecated in C99 and removed from C11.
IMO, scanf is not a good function to use when you don't know the number of elements before-hand, I suggest fgets:
#include <stdio.h>
#include <string.h>
int main(void)
{
char str[128];
char *ptr;
fgets(str, sizeof str, stdin);
/* Remove trailing newline */
ptr = strchr(str, '\n');
if (ptr != NULL) {
*ptr = '\0';
}
/* Tokens */
ptr = strtok(str, " ");
while (ptr != NULL) {
printf("%s\n", ptr);
ptr = strtok(NULL, " ");
}
return 0;
}
gets is not recommended to use, as there is no way to tell the size of the buffer. fgets is ok here because it will stop reading when the 1st new line is encountered. You could use strtok to store all the splited words in to an array of strings, for example:
#include <stdio.h>
#include <string.h>
int main(void) {
char s[256];
char *result[10];
fgets(s, sizeof(s), stdin);
char *p = strtok(s, " \n");
int cnt = 0;
while (cnt < (sizeof result / sizeof result[0]) && p) {
result[cnt++] = p;
p = strtok(NULL, " \n");
}
for (int i = 0; i < cnt; i++)
printf("%s\n", result[i]);
return 0;
}
As most of the other answers haven't covered another thing you were asking:
strtok will not allocate temporary memory and will use your given string to replace every separator with a zero termination. This is why Good morning John becomes GoodNULLmorningNULLJohn. If it wouldn't do this, each token would print the whole rest of the string on its tail like:
result[0] = Good morning John
result[1] = morning John
result[2] = John
So if you want to keep your original input and an array of char* per word, you need 2 buffers. There is no other way around that. You also need the token buffer to stay in scope as long as you use the result array of char* pointers, else that one points to invalid memory and will cause undefined behavior.
So this would be a possible solution:
int main()
{
const unsigned int resultLength = 10;
char* result[resultLength];
memset(result, 0, sizeof result); // we should also zero the result array to avoid access violations later on
// Read the input from the console
char input[256];
fgets(input, sizeof input, stdin);
// Get rid of the newline char
input[strlen(input) - 1] = 0;
// Copy the input string to another buffer for your tokens to work as expected
char tokenBuffer[256];
strcpy(tokenBuffer, input);
// Setting of the pointers per word
char* token = strtok(tokenBuffer, " ");
for (unsigned int i = 0; token != NULL && i < resultLength; i++)
{
result[i] = token;
token = strtok(NULL, " ");
}
// Print the result
for (unsigned int i = 0; i < resultLength; i++)
{
printf("result[%d] = %s\n", i, result[i] != NULL ? result[i] : "NULL");
}
printf("The input is: %s\n", input);
return 0;
}
It prints:
result[0] = Good
result[1] = morning
result[2] = John
result[3] = NULL
result[4] = NULL
result[5] = NULL
result[6] = NULL
result[7] = NULL
result[8] = NULL
result[9] = NULL
The input is: Good morning John

c how to cut of the first x words of a string?

I have a char array:
char tmp[2048];
I want to cut of the first x words of tmp. I define a word as a sequence of characters that does not include whitespaces. I tried something like this (should cut of the first 3 words):
sscanf(tmp, "%*s %*s %*s %s", tmp);
My problem is, that '%s' stops at the first whitespace it finds. I want the new string to end at the end of the old string, not at the first whitespace.
I'm also open for other suggestions how to cut of the first x words of a string. I define a word as a sequence of characters that doesn't contain whitespaces.
Here's a rough implementation:
const char* TrimWords(const char* input, int nWords)
{
while (nWords)
{
if (!isspace(*input) && isspace(*(input + 1)))
{
nWords--;
}
input++;
}
return input;
}
TrimWords("One Two Three Four Five", 3);
// returns " Four Five" after the first 3 words are trimmed.
Detailed input validation and error checking is left to the OP.
This is just a good starting point.
use strncpy(tmp, n, tmp+m); where m and n are ints
char tmp[20] = "abcdef";
strncpy(tmp, tmp + 3, 2);
for exmaple: code above will result in decdef
You can use strtok to tokenize strings by whitespace. Something similar to this could do what you're trying to achieve:
#include <stdio.h>
#include <string.h>
int main(int argc, char *argv[])
{
// Number of words to trim
const int numWords = 2;
char tmp[1024];
char buffer[1024];
sprintf(tmp, "this is a\tsentence.");
strcpy(buffer, tmp);
char* token = strtok(buffer, " \t");
for (int i = 0; i < numWords && token; i++) {
token = strtok(NULL, " \t");
}
if (token) {
size_t len = 1024 - (token - buffer);
memmove(tmp, tmp + (token - buffer), len);
}
else {
memset(tmp, '\0', 1024);
}
// Prints "a sentence."
printf("%s", tmp);
return 0;
}
However, the use of strtok is tricky at best. I would suggest using an approach similar to that of abelenky's answer.

Function to delete all occurrences of a word in a sentence in C

I have this code which will remove the first occurrence of the word from the sentence:
#include "stdio.h"
#include "string.h"
int delete(char *source, char *word);
void main(void) {
char sentence[500];
char word[30];
printf("Please enter a sentence. Max 499 chars. \n");
fgets(sentence, 500, stdin);
printf("Please enter a word to be deleted from sentence. Max 29 chars. \n");
scanf("%s", word);
delete(sentence, word);
printf("%s", sentence);
}
int delete(char *source, char *word) {
char *p;
char temp[500], temp2[500];
if(!(p = strstr(source, word))) {
printf("Word was not found in the sentence.\n");
return 0;
}
strcpy(temp, source);
temp[p - source] = '\0';
strcpy(temp2, p + strlen(word));
strcat(temp, temp2);
strcpy(source, temp);
return 1;
}
How would I modify it to delete all occurrences of the word in the given sentence? Can i still use the strstr function in this case?
Thanks for the help!
Open to completely different ways of doing this too.
P.S. This might sound like a homework question, but it's actually a past midterm question which I'd like to resolve to prepare for my midterm!
As a side question, if I use fgets(word, 30, stdin) instead of scanf("%s", word), it no longer works and tells me that the word was not found in the sentence. Why?
Try the following
#include <stdio.h>
#include <string.h>
size_t delete( char *source, const char *word )
{
size_t n = strlen( word );
size_t count = 0;
if ( n != 0 )
{
char *p = source;
while ( ( p = strstr( p, word ) ) != NULL )
{
char *t = p;
char *s = p + n;
while ( ( *t++ = *s++ ) );
++count;
}
}
return count;
}
int main( void )
{
char s[] = "abxabyababz";
printf( "%zu\n", delete( s, "ab" ) );
puts( s );
return 0;
}
The output is
4
xyz
As for the question about fgets then it includes the new line character in the string. You have to remove it from the string.
How would I modify it to delete all occurrences of the word in the given sentence?
There are many ways, as you have suggested, and since you are Open to completely different ways of doing this too...
Here is a different idea:
A sentence uses white space to separate words. You can use that to help solve the problem. Consider implementing these steps using fgets(), strtok() and strcat() to break apart the string, and reassemble it without the string to remove.
0) create line buffer sufficient length to read lines from file
(or pass in line buffer as an argument)
1) use while(fgets(...) to get new line from file
2) create char *buf={0};
3) create char *new_str; (calloc() memory to new_str >= length of line buffer)
4) loop on buf = strtok();, using " \t\n" as the delimiter
Inside loop:
a. if (strcmp(buf, str_to_remove) != 0) //approve next token for concatenation
{ strcat(new_str, buf); strcat(new_str, " ");}//if not str_to_remove,
//concatenate token, and a space
5) free allocated memory
new_str now contains sentence without occurrences of str_to_remove.
Here is a demo using this set of steps (pretty much)
int delete(char *str, char *str_to_remove)
{
char *buf;
char *new_str;
new_str = calloc(strlen(str)+1, sizeof(char));
buf = strtok(str, " \t\n");
while(buf)
{
if(strcmp(buf, str_to_remove) != 0)
{
strcat(new_str, buf);
strcat(new_str, " ");
}
buf = strtok(NULL, " \t\n");
}
printf("%s\n", new_str);
free(new_str);
getchar();
return 0;
}
int main(void)
{
delete("this sentence had a withh bad withh word", "withh");
return 0;
}

Resources