printing after splitting a line of text at delimiters - c

I am having trouble printing this. When I requested the user to insert an int it worked, but when attempting to switch it over to a char input it got screwy and won't print anything.
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
int main() {
const char delim[2] = ",";
char *token;
int j = 0;
char *hh;
FILE *ptr_file;
char buf[1000];
ptr_file = fopen("input.txt", "r");
if (!ptr_file)
return 1;
char *pt[] = { "H", "He", "Li", "Be", "B", "C", "N", "O", "F", "Ne", "Na" };
printf("what element do you want(Use atomic number)");
scanf("%s", &hh);
for (j = 0; j <= 3; j++) {
if (hh == pt[j]) {
fgets(buf, 1000, ptr_file);
token = strtok(buf, delim);
while (token != NULL) {
printf("%s\n", token);
token = strtok(NULL, delim);
}
break;
} else {
fgets(buf, 1000, ptr_file);
continue;
}
}
fclose(ptr_file);
return 0;
}

The major problem here is, you are passing scanf() the address of an uninitialized pointer hh. scanf invokes undefined behavior trying to store the word at that address.
You should make hh an array, like char hh[8] = {0}; and use scanf() this way:
scanf("%7s", hh); // to avoid buffer overflow
That said,
if(hh == pt[j]) is not the way to compare strings. You need to use strcmp() to do that, and write if (strcmp(hh, pt[j]) == 0).
you should also check the return value of scanf() to verify if input was correctly converted.

The code
char *hh;
//...
scanf("%s", &hh);
it is UB due to hh uninitialized: it is pointing to garbage.
Furthermore "%s" format specifier wants char * as passed parameter: you are passing char **
should be
char *hh = malloc(MAX_STRING_LEN);
if (hh != NULL)
{
//...
scanf("%s", hh);
}
free(hh);
or simply
char hh[MAX_STRING_LEN] = {0};
//...
scanf("%s", hh);
Into both examples MAX_STRING_LEN expresses the maximum accepted length of string + 1 for null terminator.
Using c you cannot compare strings using logical operator ==, so the code
if(hh == pt[j])
is comparing addresses not the strings.
You can use strcmp to do that.

Related

Efficient Memory allocation and freeing question

I am writing a program that prints a string having been removed white space.
At this time, in the removeWhiteSpace function, I would like to know if memory allocation and freeing are used efficiently.
If not, can you suggest appropriate improvements:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
char *removeWhiteSpace();
int main()
{
char *inputString = malloc(sizeof(char) * 100);
scanf(" %[^\n]s", inputString);
removeWhiteSpace(inputString);
printf("%s\n", inputString);
free(inputString);
return 0;
}
char *removeWhiteSpace(char *inputString)
{
char *tempArray = malloc(sizeof(char) * 100);
char *tempPointer = strtok(inputString, " ");
while (tempPointer != NULL)
{
strcat(tempArray, tempPointer);
tempPointer = strtok(NULL, " ");
}
strcpy(inputString, tempArray);
free(tempArray);
return tempArray;
}
I would do a tortoise and hare algorithm.
Copy only characters that are not space.
char *removeWhiteSpace(char *inputString) {
char *in = inputString;
char *out = inputString;
// small optimization - do not copy to itself
while (*in != '\0' && *in != ' ') {
in++;
out++;
}
for (; *in != '\0'; in++) {
if (*in != ' ') {
*out++ = *in;
}
}
*out = '\0';
return inputString;
}
Dynamic allocation takes some cost. Dynamically allocating such small fixed-size arrays doesn't look efficient. Also the return value of removeWhiteSpace is meaningless because it refers to a buffer that is already freed.
Simply allocate the arrays statically and have removeWhiteSpace return nothing.
Also note that:
You should declare arguments of functions to avoid mistakes.
You have to initialize the array tempArray before using strcat for that. Using initializer won't be efficient because it wll initialize all elements while only initializing the first element is required for strcat().
You should limit the maximum length to read via scanf() to avoid buffer overrun.
You won't need s in the scanf() format because input is specified by [ and there are no scanf() after that. The s is telling scanf() to consume s after the string.
Try this:
#include <stdio.h>
#include <string.h>
void removeWhiteSpace(char*);
int main()
{
char inputString[100];
scanf(" %99[^\n]", inputString);
removeWhiteSpace(inputString);
printf("%s\n", inputString);
return 0;
}
void removeWhiteSpace(char *inputString)
{
char tempArray[100];
char *tempPointer = strtok(inputString, " ");
tempArray[0] = '\0';
while (tempPointer != NULL)
{
strcat(tempArray, tempPointer);
tempPointer = strtok(NULL, " ");
}
strcpy(inputString, tempArray);
}

count the occurrences of each word in that phrase using C language

I am not understanding why, when I am doing sub=strtok(NULL,delim);, instead of going to the next word, it is making it null. Please try this code and help me resolve the problem.
#include <stdio.h>
#include <string.h>
int main()
{
char str[100],str1[100],*sub,*sub1;
int c1;
printf("\nEnter the string or sentence: ");
scanf("%[^\n]%*c",str);
strcpy(str1,str);
char delim[]=" ";
sub=strtok(str,delim);
while(sub !=NULL)
{
c1=0;
sub1=strtok(str1,delim);
while(sub1 !=NULL)
{
if(!strcmp(sub,sub1))
{
c1++;
}
sub1=strtok(NULL,delim);
}
printf("\n%s : %d",sub,c1);
sub=strtok(NULL,delim);
}
return 0;
}
You can't parse multiple string buffers concurrently with strtok(), like you are trying to do. Internally, strtok() maintains a static reference to the input string, that is how it knows which string to advance through when subsequent calls specify a NULL string pointer.
Before entering your outer loop, you call strtok(str) to set the internal reference to str. Then, inside the loop, you call strtok(str1) to reset that reference to str1. So, on the 1st iteration of your outer loop, before entering the inner loop, strtok() has already lost its reference to your str buffer since you replace it with the str1 buffer. Then, after the inner loop has finished scanning str1, your outer loop breaks when calling sub=strtok(NULL,delim); because there are no more tokens to read from str1.
For what you are attempting, use strtok_r() instead:
The strtok_r() function is a reentrant version strtok(). The saveptr argument is a pointer to a char * variable that is used internally by strtok_r() in order to maintain context between successive calls that parse the same string.
...
Different strings may be parsed concurrently using sequences of calls to strtok_r() that specify different saveptr arguments.
Also, because strtok(_r)() is destructive to the input string, inserting '\0' after each token found, you need to reset the contents of your 2nd string buffer back to the original string before entering the inner loop. Otherwise, the inner loop will stop scanning after is reads the 1st word in your 2nd string buffer.
Try something more like this:
#include <stdio.h>
#include <string.h>
int main()
{
char orig[100], str1[100], str2[100], *sub1, *sub2, *save1, *save2;
char delim[] = " ";
int c1;
printf("\nEnter the string or sentence: ");
scanf("%[^\n]%*c", orig);
strcpy(str1, orig);
sub1 = strtok_r(str1, delim, &save1);
while (sub1 != NULL)
{
c1 = 0;
strcpy(str2, orig);
sub2 = strtok_r(str2, delim, &save2);
while (sub2 != NULL)
{
if (strcmp(sub1, sub2) == 0)
{
++c1;
}
sub2 = strtok_r(NULL, delim, &save2);
}
printf("\n%s : %d", sub1, c1);
sub1 = strtok_r(NULL, delim, &save1);
}
return 0;
}
Otherwise, you can use a single loop through a single string buffer, storing the word counts in a separate array, eg:
#include <stdio.h>
#include <string.h>
typedef struct _wordInfo
{
char* str;
int count;
} wordInfo;
int main()
{
char str[100], *sub;
char delim[] = " ";
wordInfo words[100], *word;
int numWords = 0;
printf("\nEnter the string or sentence: ");
scanf("%[^\n]%*c", str);
sub = strtok(str, delim);
while (sub != NULL)
{
word = NULL;
for (int i = 0; i < numWords; ++i)
{
if (strcmp(sub, words[i].str) == 0)
{
word = &words[i];
break;
}
}
if (!word)
{
if (numWords == 100) break;
word = &words[numWords++];
word->str = sub;
word->count = 1;
}
else {
word->count++;
}
sub = strtok(NULL, delim);
}
for(int i = 0; i < numWords; ++i)
{
word = &words[i];
printf("\n%s : %d", word->str, word->count);
}
return 0;
}

Operations with text and delimiters

The task is: to read the text from file and to read an array of delimiters from keyboard. Than the program should search the sequence of delimiters in the text and, if it would be found 3 times or more, swap all the odd strings in a circle. Also it should detete all the words, which exceed the length limit, entered by user, but only in odd strings.
This is what i've got now:
#include "stdafx.h"
#include <stdio.h>
#include <conio.h>
int main(void)
{
setlocale(LC_ALL, "Russian"); //entering the text
const int numberOfCharactersToRead = 128;
char* inputText = (char*)(malloc(sizeof(char) * numberOfCharactersToRead));
FILE *fp;
fopen_s(&fp, "D:\texxxt.txt", "r");
if (fp == NULL)
{
printf("nFile not foundn");
system("pause");
return 0;
}
fgets(inputText, numberOfCharactersToRead, fp);
printf("Enter the sequence of delimiters: "); //entering delimiters
const int numberOfDelimitersToRead = 6;
char* delimiters = (char*)(malloc(sizeof(char) * numberOfDelimitersToRead));
int indexer = 0;
for (indexer = 0; indexer < numberOfDelimitersToRead; indexer++)
{
delimiters[indexer] = getchar();
}
//Trying to use strtok in order to devide text into rows (unsuccesful)
char delims[] = "/n";
char *pch = strtok_s(NULL, inputText, &delims);
printf("nLexems:");
while (pch != NULL)
{
printf("n%s", pch);
pch = strtok_s(NULL, inputText, &delims);
}
return 0;
}
int symcount(void)
{ //function searching the quantity of delimiters
char str[20], ch;
int count = 0, i;
printf("nEnter a string : ");
scanf_s("%s", &str);
printf("nEnter the character to be searched : ");
scanf_s("%c", &ch);
for (i = 0; str[i] != ''; i++)
{
if (str[i] == ch)
count++;
}
if (count == 0)
printf("nCharacter '%c'is not present", ch);
else
printf("nOccurence of character '%c' : %d", ch, count);
return (0);
}
I dont really know how to devide the text into rows and how to make my program differentiate even and odd strings. I'm really confused
The definition of strtok_s is the following:
char *strtok_s(char *strToken, const char *strDelimit, char **context);
You are mixing up the parameters. The 1st parameter should be a pointer to the input string and the 2nd parameter should be the delimiter string. Finally after the function is executed the 3rd parameter will be passed a pointer to the string after the position where the delimiter was found, or NULL if no delimiter was found. This pointer can then be passed onto the next strtok_s call to continue the search.
char *pchNext;
char *pch = strtok_s(inputText, delimiters, &pchNext);
while (pch != NULL)
{
printf("\n%s", pch);
pch = strtok_s(NULL, delimiters, &pchNext); // The first parameter can be NULL here
}
Also, the textual representation of the newline character is \n, not /n or n.

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

split string value in arrays

I have to split a string-input value where-ever there is a blankspace and output the result.
eg: input:
I am a noob at C
output:
>>I
>>am
>>a
>>noob
>>at
>>C
Code:
void splitText(){
char str[100];
char sep[] = " \r\n";
char *res; //if i remove this
fgets(str,sizeof str,stdin);
if (fgets(str, sizeof str, stdin) == NULL) {
printf("error");
}
char *p = strchr(str, '\n');
if (p) *p = 0;
res = strtok(str, sep); //and this
printf("%s\n",res); //and change this to str
}
Working code for anyone encountering the same problem:
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
void splitText() {
char str[100];
char sep[] = " \n";
char *res;
fgets(str,sizeof str, stdin);
if ( fgets(str, sizeof str, stdin) == NULL ) {
printf("Error");
break;
}
res = strtok(str, sep);
while(res != NULL){
printf("Splitted String: \"%s\"\n",res);
res = strtok(NULL,sep);
}
}
Thanks to everyone who contributed in helping me with this issue!
The problem with
char str[100] = scanf("%s",str);
is that you are assigning an int to a char array.
scanf() returns the number of items successfully scanned. The actual reading of chars into the array is done by scanf() itself. So you just need to call scanf() separately.
if (scanf("%s",str) != 1) { /* error */}
But scanf() is not the right tool here since you want to read a whole line. scanf() would stop at the first whitespace (after reading non-whitespace chars).
So when you type "I am a noob at C", scanf() will only read the I and ignore the rest.
What you want is to use the fgets() function to read a line:
char str[100];
if (fgets(str, sizeof str, stdin) == NULL) {
/* error */
}
/* rest of the code */
fgets() would read the newline as well if there's space in the buffer. If this is undesirable, then you can remove it:
char *p = strchr(str, '\n');
if (p) *p = 0; //remove the trailing newline.
Note: strtok() is not a thread safe function. POSIX provides strtok_r() as a thread-safe alternative. This is something to be aware of even if it doesn't matter in this specific case.
Here's a self contained example:
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
int main(void) {
char str[100];
char sep[] = " \n";
char *res;
if ( fgets(str, sizeof str, stdin) == NULL ) {
exit(1);
}
res = strtok(str, sep);
while(res != NULL){
printf("Splitted String: \"%s\"\n",res);
res = strtok(NULL,sep);
}
return 0;
}
That is not how scanf() works.
Change the code to
char str[100];
scanf("%s",str);
A little note about scanf()
You should check for return values, like here for scanf().
if (scanf("%s", str) != 1)
{
printf("scanf failed");
exit(0);
}
You should also mention the number of chars to be read by scanf() to avoid buffer overflow.
scanf("%99s", str)
For a char str[100] of size 100, one should give 99 to keep place for the null character \0.

Resources