split a string by a character without strtok in c - c

I need to write code that reads a string of characters such as jasf#fjaf#afsj to a single dimension string and then ask for a separation character (eg: #) so it will get an output in two dimensions and for every line, it will be the words between the separation character like:
jasf
fjaf
afsj
I tried:
#include <stdio.h>
#include <string.h>
void main {
int s, k, b;
printf("please enter a long string\n");
gets(longstring);
s = strlen(longstring);
printf("please choose seperationg charcter\n");
scanf("%c", &ch);
if ((ch < 'A') || ((ch > 'Z') && (ch < 'a')) || (ch > 'z')) {
for (k = 0; k < s; k++) {
for (b = 0; longstring[k] == ch; ++b) {
strcpy(mat[b], longstring);
}
}
puts(mat[b]);
}

Your code is incomplete: the function definition for main lacks its argument list, which is not optional in C, longstring is not defined, etc.
Futhermore, your method is too complicated: you do not need to test for letters if the goal is just to output one line for each part of the string between separators.
Here is a simple solution:
#include <stdio.h>
#include <string.h>
int main() {
char longstring[256];
int i, len;
char sep;
printf("please enter a long string\n");
if (fgets(longstring, sizeof longstring, stdin)) {
len = strlen(longstring);
printf("please choose a separationg character: ");
if (scanf("%c", &sep) != 1)
return 1;
for (i = 0; i < len; i++) {
if (longstring[i] == sep)
putchar('\n');
else
putchar(longstring[i]);
}
}
return 0;
}

since your code is not complete , let me add what is missing to achieve the task :
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <assert.h>
char** str_split(char* a_str, const char a_delim)
{
char** result = 0;
size_t count = 0;
char* tmp = a_str;
char* last_comma = 0;
char delim[2];
delim[0] = a_delim;
delim[1] = 0;
/* Count how many elements will be extracted. */
while (*tmp)
{
if (a_delim == *tmp)
{
count++;
last_comma = tmp;
}
tmp++;
}
/* Add space for trailing token. */
count += last_comma < (a_str + strlen(a_str) - 1);
/* Add space for terminating null string so caller
knows where the list of returned strings ends. */
count++;
result = (char**) malloc(sizeof(char*) * count);
if (result)
{
size_t idx = 0;
char* token = strtok(a_str, delim);
while (token)
{
assert(idx < count);
*(result + idx++) = strdup(token);
token = strtok(0, delim);
}
assert(idx == count - 1);
*(result + idx) = 0;
}
return result;
}
int main()
{
char longstring[1024];
char** tokens;
char ch;
unsigned long s;
printf("please enter a long string\n");
gets(longstring);
s = strlen(longstring);
printf("please choose seperationg charcter\n");
scanf("%c", &ch);
if ((ch<'A') || ((ch>'Z') && (ch<'a')) || (ch>'z'))
{
tokens = str_split(longstring, ch);
if (tokens)
{
int i;
for (i = 0; *(tokens + i); i++)
{
printf("%s\n", *(tokens + i));
free(*(tokens + i));
}
printf("\n");
free(tokens);
}
}
return 0;
}

Related

Functions works only 1 time

I've encountered a problem with my code where the function I made rotate_left that's supposed to take the first word in a given string and puts it at the end of that string and that function works but when it tried to activate it twice it doesn't do anything other than printing the same result the first function printed anyone got any ideas about it?
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <string.h>
#define MAX 80
void rotate_left(char str[])
{
int j = 0;
int i = 0;
char Temp[MAX];
char Temp2[MAX];
while (str[i] != ' ')
{
i++;
}
Temp[i] = '\0';
strncpy(Temp, str, i);
strcat(str, " ");
while (str[i] != '\0')
{
Temp2[j] = str[i];
i++;
j++;
}
Temp2[j] = '\0';
strcat(Temp2, Temp);
printf("%s\n", Temp2);
}
int main()
{
char str[MAX];
char Temp2[MAX];
printf("Enter Your String To Swtich The : ");
gets(str);
rotate_left(str);
rotate_left(str);
rotate_left(str);
return 0;
}
You have two bugs in these codes :
First you must copy the rotated string in the passed one, printing the result is not sufficient. Next you must remove the space at the beginning of the extracted word otherwise the new string start with a space.
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <string.h>
#define MAX 80
void rotate_left(char str[])
{
int j = 0;
int i = 0;
char Temp[MAX];
char Temp2[MAX];
// If the first space is not removed (see below), the next time
// the function is called, this loop stops immediately. It's why
// the second rotation didn't occur
while (str[i] != ' ')
{
i++;
}
Temp[i] = '\0';
strncpy(Temp, str, i);
strcat(str, " ");
i++; //<====== removes the space
while (str[i] != '\0')
{
Temp2[j] = str[i];
i++;
j++;
}
Temp2[j] = '\0';
strcat(Temp2, Temp);
strncpy(str, Temp2, strlen(Temp2) + 1); //<====== copies the string
printf("%s\n", str);
}
int main()
{
char str[MAX] = "Enter Your String To Switch The";
char Temp2[MAX];
for (int i=0; i<7; i++) rotate_left(str);
return 0;
}
Output :
Your String To Switch The Enter
String To Switch The Enter Your
To Switch The Enter Your String
Switch The Enter Your String To
The Enter Your String To Switch
Enter Your String To Switch The
Your String To Switch The Enter
I didn't handle special cases (if there is no space in the provided string for instance) and I removed the interactive dimension (gets) for the example.
Your code has issues:
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <string.h>
#define MAX 80
void rotate_left(char str[])
{
int j = 0;
int i = 0;
char Temp[MAX];
char Temp2[MAX];
while (str[i] != ' ')
{
i++;
}
in the code below you are having a security leak and a potential problem because you have temp variables in the stack and you are touching these without boundary checks: Example of a buffer overflow leading to a security leak
you need to write code with boundary checks especially when the variable is in the stack:
while (i < MAX /*or i < sizeof(str)*/ && str[i] != ' ' && str[i] != '\0' /*Null check also?*/ )
{
i++;
}
If you call this method with a string without space in it then god knows what will it result,
There are other issues as well like "strncpy(str, Temp2, strlen(Temp2) + 1);" this is not how this method is intended to be called as it should be "strncpy(str, Temp2, sizeof(str));": https://linux.die.net/man/3/strncpy
Let me write you sample code it will be faster for you to track some of the missing checks:
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#define MAX 80
/* just your code with safety checks so you can track why it fails */
int rotate_left(char str[], const size_t inputSize)
{
int j = 0;
int i = 0;
char Temp[MAX];
char Temp2[MAX];
while (i < inputSize && str[i] != ' ' && str[i] != '\0')
{
i++;
}
if (i == inputSize || str[i] == '\0')
{
// string without any space in it so no need to rotate?
return 0;
}
if (i + 1/*+1 to include null*/ >= MAX) {
// We dont have enough memory to handle this request
return -1;
}
strncpy(Temp, str, i);
Temp[i] = '\0';
++i;
while (i < inputSize && j < MAX && str[i] != '\0')
{
Temp2[j] = str[i];
i++;
j++;
}
if (i == inputSize) {
// String is not null terminated?
return -1;
}
if (j + 2 /*Include space and null character*/ >= MAX) {
// We dont have enough memory to handle this request
return -1;
}
Temp2[j] = ' ';
Temp2[j + 1] = '\0';
strncat(Temp2, Temp, sizeof(Temp2));
strncpy(str, Temp2, inputSize); //<====== copies the string
printf("%s\n", str);
return 0;
}
/* bit more optimization */
errno_t rotate_left_optimized(char str[], const size_t inputSize)
{
errno_t lastCall;
char temp[MAX];
int i = 0;
while (i < inputSize && str[i] != ' ' && str[i] != '\0')
{
i++;
}
if (i == inputSize || str[i] == '\0')
{
// string without any space in it so no need to rotate?
return 0;
}
const size_t inputLen = strnlen(str, inputSize);
if (i + 1 /* Include lazy space character */>= sizeof(temp)) {
// We dont have enough memory to handle this request
return -1;
}
// hidden assert(str[i] == ' ');
temp[0] = ' ';
memcpy(temp + 1, str, i);
memmove(str, str + i + 1, inputLen);
lastCall = strncat_s(str, inputSize, temp, i + 1);
if (!lastCall)
{
return lastCall;
}
return 0;
}
int main()
{
char str[MAX] = "Enter Your String To Switch The";
for (int i = 0; i < 7; i++) {
if (rotate_left_optimized(str, MAX)) {
perror("failed to rotate");
exit(EXIT_FAILURE);
}
printf("%s\n", str);
}
return 0;
}

how do i remove the similar word in strings?

I have program to remove the similar words from string but this program only removing at once word not a repeating words.
For example input:
sabunkerasmaskera kera
and should an output:
sabunmas
This my code:
#include <stdio.h>
#include <string.h>
void remove(char x[100], char y[100][100], char words[100]) {
int i = 0, j = 0, k = 0;
for (i = 0; x[i] != '\0'; i++) {
if (x[i] == ' ') {
y[k][j] = '\0';
k++;
j = 0;
} else {
y[k][j] = x[i];
j++;
}
}
y[k][j] = '\0';
j = 0;
for (i = 0; i < k + 1; i++) {
if (strcmp(y[i], kata) == 0) {
y[i][j] = '\0';
}
}
j = 0;
for (i = 0; i < k + 1; i++) {
if (y[i][j] == '\0')
continue;
else
printf("%s ", y[i]);
}
printf ("\n");
}
int main() {
char x[100], y[100][100], kata[100];
printf ("Enter word:\n");
gets(x);
printf("Enter word to remove:\n");
gets(words);
remove(x, y, words);
return 0;
}
My program output its:
sabunkerasmaskerara
and that should not be the case. Maybe I need your opinion to fixed this program and also I need help to make it better.
Your solution does not work because it uses strcmp to compare the string portions, which only works if the substring is at the end of the string, as this makes it null-terminated.
You should instead use strstr to locate the matches and use memmove to shift the string contents.
There are other issues in your code:
do not use gets()
y is unnecessary for this task.
words is not defined
Here is a modified version:
#include <stdio.h>
#include <string.h>
char *remove_all(char *str, const char *word) {
size_t len = strlen(word);
if (len != 0) {
char *p = str;
while ((p = strstr(p, word)) != NULL) {
memmove(p, p + len, strlen(p + len) + 1);
}
}
return str;
}
int main() {
char str[100], word[100];
printf ("Enter string:\n");
if (!fgets(str, sizeof str, stdin))
return 1;
printf("Enter word to remove:\n");
if (!fgets(word, sizeof word, stdin))
return 1;
word[strcspn(word, "\n")] = '\0'; // strip the trailing newline if any
remove_all(str, word);
fputs(str, stdout);
return 0;
}

accepting case-insensitive input and printing them out correctly

Hi guys I wrote a better and improved hangman game that accepts lowercase input and uppercase input.
The only problem that I have is that for example if the String looks like this "tnt" it prints out every character big in the console. I am trying to make it print out the exact same string.
So if I input big 'T' then it should accept it and output small 't'.
Could someone help me?
Thank you in advance.
#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>
#include <string.h>
#include <ctype.h>
#define MAX_WORD 100
int get_word(char *word, int size) {
fgets(word, size, stdin);
int len = strlen(word);
if (len && word[len - 1] == '\n')
word[--len] = '\0';
for (int i = 0; i < len; ++i)
if (isalpha(word[i]))
word[i] = toupper(word[i]);
else
return -1;
return len;
}
void create_output(char *output, int len) {
for (int i = 0; i < len; ++i)
output[i] = '_';
output[len] = '\0';
}
void print_output(const char *output, const char *alpha, int tries) {
printf("(%d) ", tries);
for (int i = 0; output[i]; ++i)
printf("%c ", output[i]);
printf(" %s\n", alpha);
}
bool play_game(const char *word, int len) {
char alpha[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
char output[MAX_WORD];
create_output(output, len);
int tries = 0, guessed = 0;
while (tries < len && guessed < len) { // using word length for number of tries (?!)
print_output(output, alpha, tries);
printf("Guess: ");
char guess;
scanf(" %c", &guess);
guess = toupper(guess);
if (alpha[guess - 'A'] != ' ') {
for (int i = 0; i < len; ++i)
if (word[i] == guess && output[i] == '_') {
output[i] = word[i];
++guessed;
}
alpha[guess - 'A'] = ' ';
++tries;
}
}
print_output(output, alpha, tries);
return guessed == len;
}
int main() {
printf("Enter word to guess: ");
char word[MAX_WORD];
int len = get_word(word, sizeof word);
if (len == -1) {
printf("The word cannot contain spaces.\n");
return EXIT_FAILURE;
}
if (play_game(word, len))
printf("You win!\n");
else
printf("You lose.\nThe word was %s.\n", word);
return 0;
}

Splitting a string to an array of strings

I'm trying to split a sentence the user inputs to an array of words so I can later manipulate the words separately as strings.
The code is compiling but prints only garbage after the user input.
I tried debugging but don't see the problem. Can someone help me fix it?
#include <stdio.h>
#include <string.h>
int main() {
char str[1000];
int i = 0;
char rev[1000][1000];
int r = 0;
puts("Enter text:");
gets(str);
int k, length = 0;
printf_s("So the words are:\n");
while (str[i] != '\0') {
if (str[i] == ' ') {
k = i - length;
do {
rev[r][k] = (str[k]);
k++;
} while (str[k] != ' ');
printf(" ");
length = (-1);
r++;
} else
if (str[i + 1] == '\0') {
k = i - length;
do {
rev[r][k] = (str[k]);
k++;
} while (str[k] != '\0');
length = 0;
r++;
}
length++;
i++;
}
for (int r = 0; r < 1000; r++)
printf("%s ", rev[r]);
return 0;
}
fix like this
#include <stdio.h>
int main(void) {
char str[1000];
char rev[1000][1000];
puts("Enter text:");
fgets(str, sizeof str, stdin);//Use fgets instead of gets. It has already been abolished.
int r = 0;
int k = 0;
for(int i = 0; str[i] != '\0'; ++i){
if (str[i] == ' ' || str[i] == '\n'){//is delimiter
if(k != 0){
rev[r++][k] = '\0';//add null-terminator and increment rows
k = 0;//reset store position
}
} else {
rev[r][k++] = str[i];
}
}
if(k != 0)//Lastly there was no delimiter
rev[r++][k] = '\0';
puts("So the words are:");
for (int i = 0; i < r; i++){
printf("%s", rev[i]);
if(i < r - 2)
printf(", ");
else if(i == r - 2)
printf(" and ");
}
return 0;
}
Replace you declaration
char rev[1000][1000];
with
char * rev[1000]; // We will need pointers only
int i = 0; // Index to previous array
and all your code after
puts( "Enter text:" );
with this:
fgets( str, 998, stdin ); // Safe way; don't use gets(str)
const char delim[] = ",; "; // Possible delimiters - comma, semicolon, space
char *word;
/* Get the first word */
word = strtok( str, delim );
rev[i++] = word;
/* Get the next words */
while( word != NULL )
{
word = strtok( NULL, delim );
rev[i++] = word;
}
/* Testing */
for (int r = 0; r < i - 1; r++)
printf( "%s\n", rev[r] );
return 0
}
As you can see, all dirty work is done with the strtok() function ("string to tokens") which walks through other and other words ("tokens"), recognizing them as delimited by one or more characters from the string delim.
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
int count_spaces(char *str)
{
if (str == NULL || strlen(str) <= 0)
return (0);
int i = 0, count = 0;
while (str[i])
{
if (str[i] == ' ')
count++;
i++;
}
return (count);
}
int count_char_from_pos(char *str, int pos)
{
if (str == NULL || strlen(str) <= 0)
return 0;
int i = pos, count = 0;
while (str[i] && str[i] != ' ')
{
count++;
i++;
}
return count;
}
char **get_words(char *str)
{
if (str == NULL || strlen(str) <= 0)
{
printf("Bad string inputed");
return NULL;
}
int i = 0, j = 0, k = 0;
char **dest;
if ((dest = malloc(sizeof(char*) * (count_spaces(str) + 1))) == NULL
|| (dest[0] = malloc(sizeof(char) * (count_char_from_pos(str, 0) + 1))) == NULL)
{
printf("Malloc failed\n");
return NULL;
}
while (str[i])
{
if (str[i] == ' ') {
dest[j++][k] = '\0';
if ((dest[j] = malloc(sizeof(char) * (count_char_from_pos(str, i) + 1))) == NULL)
{
printf("Malloc failed\n");
return NULL;
}
k = 0;
}
else {
dest[j][k++] = str[i];
}
i++;
}
dest[j][k] = 0;
dest[j + 1] = NULL;
return dest;
}
int main(void) {
char *line = NULL;
size_t n = 0;
getline(&line, &n, stdin);
printf("%s\n", line);
line[strlen(line) - 1] = 0;
printf("%s\n", line);
char **tab = get_words(line);
int i = 0;
while (tab[i])
{
printf("%s\n", tab[i++]);
}
}
here is a long but fully working example
get the user input
then send it to get_words function. It will get the number of words, the number of characters for each words, allocate everything in memory and writes chars then return it. You get a char ** and prints it just tested it it works
If you wish to split a string into an array of strings, you should consider the strtok function from #include <string.h>. The strtok function will the split the string on the given delimiter(s). For your case, it would the " ".
Using the strtok example from Tutorials Point:
#include <string.h>
#include <stdio.h>
int main(){
char str[80] = "This is - www.tutorialspoint.com - website";//The string you wish to split
const char s[] = "-";//The thing you want it to split from. But there is no need to this.
char *token;//Storing the string
/* get the first token */
token = strtok(str, s);//Split str one time using the delimiter s
/* walk through other tokens */
while( token != NULL )
{
printf( " %s\n", token );//Print the string
token = strtok(NULL, s);//Split the string again using the delimiter
}
return(0);
}

Program that returns words that ends and starts with the same letter

I have problem with my alignement. This time I want my program to return words that ends and starts with the same letter. I've wrote something like this, but it seems to return random words.
#include <stdio.h>
#include <string.h>
void main()
{
char str[100];
int i, t, j, len;
printf("Enter a string : ");
scanf("%[^\n]s", str);
len = strlen(str);
str[len] = ' ';
for (t = 0, i = 0; i < strlen(str); i++)
{
if ((str[i] == ' ') && (str[i - 1] == str[0]))
{
for (j = t; j < i; j++)
printf("%c", str[j]);
t = i + 1;
printf("\n");
}
else
{
if (str[i] == ' ')
{
t = i + 1;
}
}
}
}
You can use strtok to split the strings from stdin, then apply a letter checker on each parsed word one at a time.
Something like this:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#define MAXCHAR 100
int is_start_end(char *word);
void exit_if_null(void *ptr, const char *msg);
int
main(void) {
char str[MAXCHAR];
char *word;
char **all_words;
int words_size = 1, word_count = 0;
int i, found;
all_words = malloc(words_size * sizeof(*all_words));
exit_if_null(all_words, "initial Allocation");
printf("Enter words(enter empty line to terminate):\n");
while (fgets(str, MAXCHAR, stdin) != NULL && strlen(str) != 1) {
word = strtok(str, " \n");
while (word !=NULL) {
if (words_size == word_count) {
words_size *= 2;
all_words = realloc(all_words, words_size * sizeof(*all_words));
exit_if_null(all_words, "Reallocation");
}
all_words[word_count] = malloc(strlen(word)+1);
exit_if_null(all_words[word_count], "Initial Allocation");
strcpy(all_words[word_count], word);
word_count++;
word = strtok(NULL, " \n");
}
}
printf("Words that have equal first and last letters:\n");
found = 0;
for (i = 0; i < word_count; i++) {
if (is_start_end(all_words[i])) {
found = 1;
printf("%s\n", all_words[i]);
}
free(all_words[i]);
all_words[i] = NULL;
}
if (found == 0) {
printf("None Found\n");
}
free(all_words);
all_words = NULL;
return 0;
}
int
is_start_end(char *word) {
int len;
len = strlen(word);
if ((len == 1) || (tolower(word[0]) == tolower(word[len-1]))) {
return 1;
}
return 0;
}
void
exit_if_null(void *ptr, const char *msg) {
if (!ptr) {
printf("Unexpected null pointer: %s\n", msg);
exit(EXIT_FAILURE);
}
}
This line removes the null terminator of the string:
len = strlen(str);
str[len] = ' ';
thus the string no longer exists, what is left is just an ordinary array of characters.
The next call to strlen, in the body of the for loop, will cause undefined behavior.

Resources