strstr function and multi dimensional arrays in c - c

#include <stdio.h>
#include <string.h>
void find_track(char *search_for);
char tracks[][80] = {
"I left my heart in Harvard Med School",
"Newark, Newark - a wonderful town",
"Dancing with a Dork",
"From here to maternity",
"The girl from Iwo Jima"
};
int main() {
char *to_search_str;
printf("Search for: ");
fgets(to_search_str, 80, stdin);
find_track(to_search_str);
return 0;
}
void find_track(char *search_for) {
int i;
for (i=0; i<5; i++) {
if (strstr(tracks[i], search_for)) {
printf("Track %d: '%s'\n", i, tracks[i]);
}
}
}
The program is supposed to search for a string in every string in the tracks multi dimensional array but the strstr() function in the find_track is always returning null no matter the input (even if we input the a sub string of a string from tracks multi dimensional array). I don't know why this is happening?
EDIT:
After correction
#include <stdio.h>
#include <string.h>
void find_track(char *search_for);
char tracks[][80] = {
"I left my heart in Harvard Med School",
"Newark, Newark - a wonderful town",
"Dancing with a Dork",
"From here to maternity",
"The girl from Iwo Jima"
};
int main() {
char to_search_str[80];
printf("Search for: ");
fgets(to_search_str, 80, stdin);
to_search_str[strlen(to_search_str)-1] = '\0';
find_track(to_search_str);
return 0;
}
void find_track(char *search_for) {
int i;
for (i=0; i<5; i++) {
if (strstr(tracks[i], search_for)) {
printf("Track %d: '%s'\n", i, tracks[i]);
}
}
}
Output

Most likely the issue with input via fgets().
You are reading into an uninitialized pointer to_search_str, which does not point to a valid memory location. In this case, you can simply change this to an array, like char to_search_str[80] = {0}; and get done with it.
You need to trim the trailing newline that's stored in the input buffer.
From the man page, (emphasis mine)
fgets() reads in at most one less than size characters from stream and stores them into the buffer pointed to by s. Reading stops after an EOF or a newline. If a newline is read, it is stored into the buffer. A terminating null byte ('\0') is stored after the last character in the buffer.
A quick way of getting that done is to_search_str[strcspn(to_search_str, "\n")] = 0;, but there are more robust ways mentioned in this other answer

You aren't allocating to_search_str pointer, the char * pointer you pass to fgets as the destination buffer. Being it actually uninitialized, this causes undefined behavior that normally ends with a program crash.
You just need to allocate it, statically or dynamically.
The simplest solution consists in just defining a static array in the stack:
#include <string.h>
#define LEN 80
int main() {
char to_search_str[LEN];
printf("Search for: ");
fgets(to_search_str, LEN, stdin);
/* Remove trailing newline from the string to search */
to_search_str[strcspn(to_search_str, "\n")] = 0;
find_track(to_search_str);
return 0;
}
The size of the array is 80 because you use this number as the size parameter in fgets. Please note the use of a #define for the constant 80, making possible to change it in a easier way.
The dynamic allocation in the heap involves the use of malloc() function (and free() as soon as the array is not needed anymore):
#include <string.h>
#define LEN 80
int main() {
char * to_search_str = malloc(LEN);
printf("Search for: ");
fgets(to_search_str, LEN, stdin);
/* Remove trailing newline from the string to search */
to_search_str[strcspn(to_search_str, "\n")] = 0;
find_track(to_search_str);
free(to_search_str);
return 0;
}
Note: since fgets retains trailing newline ``\n'` in the output buffer, we have to remove it. I used the clever oneliner solution described here.

char *to_search_str; is an uninitialized pointer, writing to it will result in undefined behavior. You have to allocate memory or use an array instead char to_search_str[100]; for example.
Also don't forget that fgets will also read the newline into the buffer, which you have to remove.

This code snippet in main
char *to_search_str;
printf("Search for: ");
fgets(to_search_str, 80, stdin);
invokes undefined behavior because the pointer to_search_str is not initialized and has indeterminate value.
It seems you at least mean
char to_search_str[80];
printf("Search for: ");
fgets(to_search_str, 80, stdin);
The function fgets can append the new line character '\n' to the entered string.
You need to remove it for example the following way
to_search_str[ strcspn( to_search_str, "\n" ) ] = '\0';
The function find_track should be declared at least like
void find_track( const char *search_for);
Though it is a bad idea when a function definition relies on global variables.
Also the approach of finding relevant strings is not good. For example the user can enter a string that contains only one character 'a'. In this case all records will satisfy the condition. You should check that the searched string forms a word (a sequence of characters separated by spaces) in a string in the array.

Related

access an argument in a string pointer char*

I need to try and fix sentences from an input in c, so I tried separating tokens and making new strings and then I wanted to access the first char of each string and make it a capital letter.
Now I am having trouble understanding how to access only one char of each new string, like trying to access only 'e' in hello which is in str1[0] second char.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
void main()
{
char str1[601], * str2[601];
int i = 0, j = 0;
printf_s("*************** Welcome to the text cleaner ***************\n\n");
printf_s("Please enter text:\n");
gets_s(str1, sizeof(str1));
char* sentence=NULL,*next_sentence=NULL;
sentence = strtok_s(str1,".",&next_sentence);
while (sentence != NULL)
{
printf(" %s\n", sentence);
str2[i++] = sentence;
sentence = strtok_s(NULL, ".", &next_sentence);
}
str2[i++] = '\0';
printf_s("%s", str2[1]);
}
Code and content of variables in debugger
Here is my take on what you are trying to do. I'm showing the code and the results. I have simplified your effort since you are mixing printf and printf_s. You use the _s variant for buffer overflow control. That does not seem to be your concern while simply learning about arrays.
Here is the code:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
void main() {
char str1[601]; // This is an array of chars.
// If storing a string, final elem is 0x0
char *str2[601]; // This is a pointer to an array of chars.
//
int i = 0;
int j = 0;
// I removed your _s variants of standard libraries. Let's keep
// things simple.
printf("*************** Welcome to the text cleaner ***************\n\n");
printf("Please enter text:\n");
// ditto for gets to fgets
//
// Excerpt rom the manpage
//
// char *fgets(char *s, int size, FILE *stream);
//
// fgets() reads in at most one less than size characters from
// stream and stores them into the buffer pointed to by s.
// Reading stops after an EOF or a newline. If a newline is read,
// it is stored into the buffer. A terminating null byte ('\0')
// is stored after the last character in the buffer.
//
// fgets() returns s on success, and NULL on error or when end of
// file occurs while no characters have been read.
//str1 = fgets(str1, sizeof(str1), stdin);
// I would do a null check here.
if (NULL == fgets(str1, sizeof(str1), stdin)) {
return; // graceful exit
}
// Notice on the bracket print of your text, the closing >
// is shown on the next line. This is because its capturing the
// newline/carriage return character.
printf("You entered %d chars and the text was:\n<%s>\n", strlen(str1), str1);
// These are for your strtok operation
// I would call them tokens or words, but
// whatever.
char *sentence=NULL;
char *next_sentence=NULL;
// wants to parse a string
// Excerpt from manpage
//
// char *strtok(char *str, const char *delim);
// Ahh, now I see why you name is sentence. You
// are looking for periods to separage sentences.
printf("Lets use strtok\n");
sentence = strtok(str1, ".");
while (sentence != NULL) {
printf("A sentence is:\n %s\n", sentence);
str2[i++] = sentence;
sentence = strtok(NULL, ".");
}
// So now, your individual sentences are stored
// in the array str2.
// str2[0] is the first sentence.
// str2[1] is the next sentence.
//
// To access the characters, specify a sentence and
// then specify the character.
//
// You can do the math, but do a man ascii, look at
// difference in lowercase a and uppercase A in terms
// of ascii. If its not captializ3ed already, simply
// add that offset or error out if not in set a-z.
//
// Here I will just make the first letter of the second
// sentence to be J.
str2[1][0] = 'J';
// Note, since you are going to have in the 'space'
// since you are delimitting on '.', It will have the
// effect of replacing 'space' with 'J'.
printf("Sentence two is: \n%s\n", str2[1]);
}
Here is the code in action.
*************** Welcome to the text cleaner ***************
Please enter text:
John was here. and here.
You entered 25 chars and the text was:
<John was here. and here.
>
Lets use strtok
A sentence is:
John was here
A sentence is:
and here
A sentence is:
Sentence two is:
Jand here
I hope that helps. TLDR use str2[x][y] to access a string x at character y.

C - split String into strings [duplicate]

I have problem with splitting a string. The code below works, but only if between strings are ' ' (spaces). But I need to split strings even if there is any whitespace char. Is strtok() even necessary?
char input[1024];
char *string[3];
int i=0;
fgets(input,1024,stdin)!='\0') //get input
{
string[0]=strtok(input," "); //parce first string
while(string[i]!=NULL) //parce others
{
printf("string [%d]=%s\n",i,string[i]);
i++;
string[i]=strtok(NULL," ");
}
A simple example that shows how to use multiple delimiters and potential improvements in your code. See embedded comments for explanation.
Be warned about the general shortcomings of strtok() (from manual):
These functions modify their first argument.
These functions cannot be used on constant strings.
The identity of the delimiting byte is lost.
The strtok() function uses a static buffer while parsing, so it's not thread
safe. Use strtok_r() if this matters to you.
#include <stdio.h>
#include<string.h>
int main(void)
{
char input[1024];
char *string[256]; // 1) 3 is dangerously small,256 can hold a while;-)
// You may want to dynamically allocate the pointers
// in a general, robust case.
char delimit[]=" \t\r\n\v\f"; // 2) POSIX whitespace characters
int i = 0, j = 0;
if(fgets(input, sizeof input, stdin)) // 3) fgets() returns NULL on error.
// 4) Better practice to use sizeof
// input rather hard-coding size
{
string[i]=strtok(input,delimit); // 5) Make use of i to be explicit
while(string[i]!=NULL)
{
printf("string [%d]=%s\n",i,string[i]);
i++;
string[i]=strtok(NULL,delimit);
}
for (j=0;j<i;j++)
printf("%s", string[i]);
}
return 0;
}

String example doesn't behave as expected

I'm learning C and I've been following the "Head First C" book. I arrived at an example where this is the resulting code:
#include <stdio.h>
#include <string.h>
char tracks[][80] = {
"I left my heart in Harvard Med School",
"Newark, Newark - A wonderful town",
"Dancing with a Dork",
"From here to maternity",
"The girl from Iwo Jima",
};
void find_track(char search_for[])
{
int i;
for(i = 0; i < 5; i++) {
if (strstr(tracks[i], search_for))
printf("Track %i: '%s'\n", i, tracks[i]);
}
}
int main()
{
char search_for[80];
printf("Search for: ");
fgets(search_for, 80, stdin);
find_track(search_for);
return 0;
}
I've double, triple, and quadruple checked. This code is exactly the same as the example in the book. At first I made it my own so that I'm not just copying without learning, but when I didn't work I literally copied it to make sure. The code askes for a string, and then prints any tracks containing the string you gave it. In the book, when the example code is run, the string given was "town", and the code prints "Newark, Newark - A wonderful town." However, when I do the exact same thing it prints nothing but a newline.
I can't stress enough, this code is exactly the same as in the book, yet it behaves differently. Can someone see why?
The problem is in this statement
fgets(search_for, 80, stdin);
Function fgets includes in the string the new line character. You have to remove it from the string. For example
size_t n = strlen( search_for );
if ( n != 0 && search_for[n-1] == '\n' ) search_for[n-1] = '\0';
From the C Standard
Description
2 The fgets function reads at most one less than the
number of characters specified by n from the stream pointed to by
stream into the array pointed to by s. No additional characters are
read after a new-line character (which is retained) or after
end-of-file. A null character is written immediately after the last
character read into the array.

The basics of using strings and substrings in C programming [closed]

Closed. This question needs to be more focused. It is not currently accepting answers.
Want to improve this question? Update the question so it focuses on one problem only by editing this post.
Closed 8 years ago.
Improve this question
I've been trying to learn C programming by reading a textbook, but am confused about how strings and substrings work.
I have an idea of what strings and substrings are from java, but can't figure out the syntax in C.
Here's a question from the book that I thought might be easy, but I can't get it.
Write and test a function hydroxide that returns a 1 for true if its string argument ends in the substring OH.
It recommends testing the function with KOH and NaCl.
Also, how would I remove and add letters at the end of the string?
Like, if for some reason I wanted to change NaCl to NaOH?
Any help and explanations would be really appreciated.
ETA:
I guess what I'm most confused on is how to make the program look at the last two letters in the string and compared them to OH.
I'm also not sure how to pass strings to functions.
String is a sequence of characters that ends with special null-terminated character '\0'. If there is no \0, functions that work with string won't stop until the \0 symbol is found. This character may happen in any place after the end of pseudo string (I mean a string without \0) and only then stop.
The following example shows the necessity of this null-terminated character:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main()
{
char string[] = "Hello!";
printf("original string:\n%s\n\n", string);
memset(string, '-', 5);
printf("memset doesn't affect the last two symbols: '!' and '\\0':\n%s", string);
memset(string, '-', 6);
printf("\n\nmemset doesn't affect the last symbol: '\\0':\n%s\n\n", string);
memset(string, '-', 7);
printf("memset affects all symbols including null-terminated one:\n%s", string);
return 0;
}
/* OUTPUT:
original string:
Hello!
memset doesn't affect the last two characters: '!' and '\0':
-----!
memset doesn't affect the last character: '\0':
------
memset affects all characters including null-terminated one:
-------#↓#
*/
Substring is a char sequence that is in a string. It may be less or equal to the string.
Suppose, "NaOH" is a string. Then substring may be: "N", "a", "O", "H", "Na", "aO", "OH", "NaO", "aOH", "NaOH". To find whether substring is in the string or not you can use strstr function. It's prototype is char * strstr ( char * str1, const char * str2 );.
This code shows this function's results:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main()
{
char *ptrCh = NULL;
ptrCh = strstr("hello", "h");
printf("ptrCh: %p\n", ptrCh);
printf("%s\n\n", ptrCh);
ptrCh = strstr("hello", "z");
printf("ptrCh: %p\n", ptrCh);
printf("%s\n\n", ptrCh);
return 0;
}
/* OUTPUT:
ptrCh: 00403024
hello
ptrCh: 00000000
(null)
*/
As for the first printf, it prints characters beginning from the position of 'h' and when it reaches null-terminated character, which is next after 'o', it stops, exactly as in previous program.
To make your program more interactive, you can declare array and then a pointer to it. Array size must be enough to store the longest formula. Suppose, 100 will be enough:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main()
{
char buf[100] = {0};
char *ptr = &buf[0];
scanf("%s", ptr);
// printf() gets a pointer as argument
printf("%s\n", ptr);
// printf() gets also a pointer as argument.
// When you pass arrays name without index to a function,
// you pass a pointer to array's first element.
printf("%s", buf);
return 0;
}
And as for rewriting letters in the end of the string. Here is a small program that does it. Pay attention at comments:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main()
{
char buf[100] = {0};
char formula[100] = {0};
char compound[100] = {0};
char *ptr = &buf[0];
char *pFormula = &formula[0];
char *pCompound = &compound[0];
printf("Enter formula: ");
scanf("%s", pFormula);
printf("Enter chemical compound: ");
scanf("%s", pCompound);
// Copying the first chemical elements without the last
// several that will be replaced by another elements.
strncpy(ptr, pFormula, strlen(pFormula) - strlen(pCompound));
// Adding new compound to the first elements.
// Function also adds a null-terminated character to the end.
strncat(ptr, pCompound, strlen(pCompound));
printf("The new chemical compound is: ");
printf("%s", ptr);
return 0;
}
/* OUTPUT:
Enter formula: NaOH
Enter chemical compound: Cl
The new chemical compound is: NaCl
*/
In C, we use null-terminated strings. That is the "invisible", 0 value. Not ASCII "0", but the zero value, like 8-bit 0x00. You can represent it in literal text with '\0' or "\0" or unquoted 0, however, in a literal string it is redundant because most functions like strcmp() or strstr() or strcat() all expect and work with null terminated strings. Null char is the stops sign for the C standard string functions.
One easy way to implement this with C library calls is to test for existence of the substring and then test that substring's length, which verify it is at end of string.
Assume buf is some big string buffer, char buf[1024] and char *temp is a temporary variable.
temp = strstr(buf, "OH") returns the pointer to "OH" if exists in buf at any offset.
strlen(temp) Get length of temp, if at end of string, it will be 2 (doesn't include null terminator), so if the original string is "OHIO" or "SOHO" it wont match because it'll be 4 and 3 respectively.
The above is the core of the code, not the full robust implementation. You need to check for valid return values, etc.
char buf[1024];
char *temp;
strcpy(buf, "NaOH");
if((temp = strstr(buf, "OH")) != 0)
{
// At this point we know temp points to something that starts with "OH"
// Now see if it is at the end of the string
if(strlen(temp) == 2)
return true; // For C99 include stdbool.h
return false;
}
You could get obscure, and check for the null terminator directly, will be a smidge quicker. This code is safe as long as it is inside the if() for strstr(), otherwise never do this if you don't know a string is a least N characters long.
if(temp[2] == '\0')
return true; // For C99 include stdbool.h
As far as appending to a string, read the docs on strcat. Keep in mind with strcat, you must have enough space already in the buffer you are appending into. It isn't like C++ std::string or Java/C# string where those will dynamically resize as needed. In C, you get to do all of that yourself.

How to correctly input a string in C

I am currently learning C, and so I wanted to make a program that asks the user to input a string and to output the number of characters that were entered, the code compiles fine, when I enter just 1 character it does fine, but when I enter 2 or more characters, no matter what number of character I enter, it will always say there is just one character and crashes after that. This is my code and I can't figure out what is wrong.
int main(void)
{
int siz;
char i[] = "";
printf("Enter a string.\n");
scanf("%s", i);
siz = sizeof(i)/sizeof(char);
printf("%d", siz);
getch();
return 0;
}
I am currently learning to program, so if there is a way to do it using the same scanf() function I will appreciate that since I haven't learned how to use any other function and probably won't understand how it works.
Please, FORGET that scanf exists. The problem you are running into, whilst caused mostly by your understandable inexperience, will continue to BITE you even when you have experience - until you stop.
Here is why:
scanf will read the input, and put the result in the char buffer you provided. However, it will make no check to make sure there is enough space. If it needs more space than you provided, it will overwrite other memory locations - often with disastrous consequences.
A safer method uses fgets - this is a function that does broadly the same thing as scanf, but it will only read in as many characters as you created space for (or: as you say you created space for).
Other observation: sizeof can only evaluate the size known at compile time : the number of bytes taken by a primitive type (int, double, etc) or size of a fixed array (like int i[100];). It cannot be used to determine the size during the program (if the "size" is a thing that changes).
Your program would look like this:
#include <stdio.h>
#include <string.h>
#define BUFLEN 100 // your buffer length
int main(void) // <<< for correctness, include 'void'
{
int siz;
char i[BUFLEN]; // <<< now you have space for a 99 character string plus the '\0'
printf("Enter a string.\n");
fgets(i, BUFLEN, stdin); // read the input, copy the first BUFLEN characters to i
siz = sizeof(i)/sizeof(char); // it turns out that this will give you the answer BUFLEN
// probably not what you wanted. 'sizeof' gives size of array in
// this case, not size of string
// also not
siz = strlen(i) - 1; // strlen is a function that is declared in string.h
// it produces the string length
// subtract 1 if you don't want to count \n
printf("The string length is %d\n", siz); // don't just print the number, say what it is
// and end with a newline: \n
printf("hit <return> to exit program\n"); // tell user what to do next!
getc(stdin);
return 0;
}
I hope this helps.
update you asked the reasonable follow-up question: "how do I know the string was too long".
See this code snippet for inspiration:
#include <stdio.h>
#include <string.h>
#define N 50
int main(void) {
char a[N];
char *b;
printf("enter a string:\n");
b = fgets(a, N, stdin);
if(b == NULL) {
printf("an error occurred reading input!\n"); // can't think how this would happen...
return 0;
}
if (strlen(a) == N-1 && a[N-2] != '\n') { // used all space, didn't get to end of line
printf("string is too long!\n");
}
else {
printf("The string is %s which is %d characters long\n", a, strlen(a)-1); // all went according to plan
}
}
Remember that when you have space for N characters, the last character (at location N-1) must be a '\0' and since fgets includes the '\n' the largest string you can input is really N-2 characters long.
This line:
char i[] = "";
is equivalent to:
char i[1] = {'\0'};
The array i has only one element, the program crashes because of buffer overflow.
I suggest you using fgets() to replace scanf() like this:
#include <stdio.h>
#define MAX_LEN 1024
int main(void)
{
char line[MAX_LEN];
if (fgets(line, sizeof(line), stdin) != NULL)
printf("%zu\n", strlen(line) - 1);
return 0;
}
The length is decremented by 1 because fgets() would store the new line character at the end.
The problem is here:
char i[] = "";
You are essentially creating a char array with a size of 1 due to setting it equal to "";
Instead, use a buffer with a larger size:
char i[128]; /* You can also malloc space if you desire. */
scanf("%s", i);
See the link below to a similar question if you want to include spaces in your input string. There is also some good input there regarding scanf alternatives.
How do you allow spaces to be entered using scanf?
That's because char i[] = ""; is actually an one element array.
Strings in C are stored as the text which ends with \0 (char of value 0). You should use bigger buffer as others said, for example:
char i[100];
scanf("%s", i);
Then, when calculating length of this string you need to search for the \0 char.
int length = 0;
while (i[length] != '\0')
{
length++;
}
After running this code length contains length of the specified input.
You need to allocate space where it will put the input data. In your program, you can allocate space like:
char i[] = " ";
Which will be ok. But, using malloc is better. Check out the man pages.

Resources