Closed. This question does not meet Stack Overflow guidelines. It is not currently accepting answers.
This question does not appear to be about programming within the scope defined in the help center.
Closed 3 years ago.
Improve this question
The goal is to create substrings from an inputted string by extracting words which are separated by spaces.
The substrings have to be variables themselves.
Sounds easy, but what makes it hard is that you can only use strcpy, strcmp, strlen, strcat, strncpy, strncmp, strnlen, and strncat.
Example:
input:
"John 40 Hitman"
driver:
...
cout << word1 << endl
<< word2 << endl
<< word3 << endl;
output:
John
40
Hitman
Here is my code
#include <iostream>
#include <cstring>
#include <stdio.h>
int main(){
const char *string = "Name Age Job";
char name[10];
char age[10];
char job[10];
int length = strlen(string);
int temp = 0;
bool flag = false;
for(int i = 0; i < length + 1; i++){
if(isspace(string[i]) && !flag){
strncpy(name, string, i);
name[i] = '\0';
temp = i;
flag = !flag;
cout << name << endl;
continue;
}
if(isspace(string[i])){
strncpy(age, string + temp + 1, length - i - 1);
age[temp - i] = '\0';
temp = i;
cout << age << endl;
continue;
}
if(string[i] == '\0'){
strncpy(job, string + temp + 1, length);
job[temp - i] = '\0';
cout << job << endl;
}
}
It works but it has to use a flag boolean, strings are not dynamic, only works for a string with 2 spaces, and there is a lot of repeated code. Overall really janky, but I spent roughly two hours on this and I do not know how to improve it.
If you are wondering, this is indeed a homework problem, but it's an intro class and my professor only wants the correct output for a hard coded string with only 3 words. I, however, want to learn how to improve upon it and would be really grateful for any help. Thanks.
All you need to do is replace space ' ' with '\0'(the string end) thus creating 3 substrings from the original. The following program does that and just dumps the string to cout but you could hold the pointers in an array as well (eg. char* substring[3]).
int main(){
char string[] = "Name Age Job";
char* temp = string;
for(char* it = string; *it; ++it ) {
if (*it == ' ') {
*it = '\0';
std::cout << temp << std::endl;
temp= it + 1;
}
}
std::cout << temp << std::endl;
}
The proper way to do this with C functions only would be to use strtok, although that one chops up a string in-place.
Regarding your code, there's lot of needless branching and checks. You should avoid continue, it is an almost 100% certain sign of a loop in need of improvements. Whenever you find yourself needing it, there's usually a better way.
You should also avoid strncpy because as you've noticed, it is a pain to keep track of when it null terminates and when it doesn't. It's a dangerous function and much slower than memcpy, which could be used instead here.
Here is a simplified version based on using 2 pointers, one all the time set to point at the next space and one all the time set to point at the beginning of the next valid word.
#include <string.h>
#include <ctype.h>
#include <stdio.h>
const char* find_next_space (const char* str)
{
while(*str != '\0' && !isspace(*str))
str++;
return str;
}
int main (void)
{
const char str[] = "hello world how are you";
char substr[5][10];
const char* word_start = str;
for(size_t i = 0; i<5; i++)
{
if(*word_start == '\0')
break;
const char* next_space = find_next_space(word_start);
size_t length = (size_t)(next_space-word_start);
memcpy(substr[i], word_start, length);
substr[i][length] = '\0';
puts(substr[i]);
word_start = next_space+1;
}
}
This code simplifies things by not checking if a string would fit and it doesn't allocate memory. Real production-quality C code wouldn't use char [5][10] but rather a pointer array char* [5] where each pointer is set to point at dynamically allocated memory.
I agree with #acraig5075 as your code is more C than C++.
If you are thinking of writing this in C++ using the STL string, one way of doing this is the following.
#include <iostream>
#include <string>
#include <sstream>
#include <vector>
std::vector<std::string> split_string( const std::string &srcString, const char &delimiterKeywrd )
{
/**
* Splits a string according to a keyword delimiter and returns a vector with the
* segments of the split string.
*
* Args:
* #param srcString: The required string to split.
* #param delimiterKeywrd: The delimiter keyword that the string will be splitted to
* #return segmentsVectr: A vector holding the segments of the srcString that was splitted.
*
*/
std::stringstream inputStr;
inputStr.str( srcString );
std::string segment;
std::vector<std::string> segmentsVectr;
while( std::getline( inputStr, segment, delimiterKeywrd ) )
{
segmentsVectr.push_back( segment );
}
return segmentsVectr;
}
int main() {
std::string inputStr{"John 40 Hitman"};
std::vector<std::string> result;
char delimiterKeywrd = ' ';
result = split_string( inputStr, delimiterKeywrd );
// Iterate through the vector and print items on a different line.
for ( const std::string &item : result )
{
std::cout << item << std::endl;
}
return 0;
}
Check std::string at cppreference and examples if you are not familiar.
Also here I am using std::stringstream which makes it easy to use std::getline.
If std::stringstream is not your preferred way you can always check this implementation from another question about splitting a string by character.
Hope this helps.
Related
So while working through a course on Udemy over C++ one of the challenges was to check a string to see whether it was a palindrome or not. I completed the task successfully but went about it a different way than the instructor. I understand there are a multitude of ways to complete a task but I am wondering which is more efficient and why? It may seem stupid to be wondering about this while reteaching myself coding but I feel this is something I should be keeping in mind.
//Instructors code//
# include<iostream>
using namespace std;
/*program for reverse a string and check a string is a palidrome
*/
int main()
{
string str="MADAM";
string rev="";
int len=(int)str.length();
rev.resize(len);
for(int i=0, j=len-1; i<len; i++, j--)
{
rev[i]=str[j];
}
rev[len]='\0';
if(str.compare(rev)==0)
cout<<"palindrome"<<endl;
else
cout<<"not a pallindrome"<<endl;
return 0;
}
My Approach
#include <iostream>
using namespace std;
int main(){
string str1="test";
// cout << "Enter a string to check if it is a Palindrome: ";
// getline(cin,str1);
string str2;
string::reverse_iterator it;
for(it=str1.rbegin(); it!= str1.rend(); it++)
{
str2.push_back(*it);
}
if(!str1.compare(str2))
cout << "\nPalindrome";
else
cout << "\nNot a Palindrome";
return 0;
}
Thank you in advance.
In theory the code from your instructor is more efficient, but both examples have issues.
With your instructors code the main issue is the use of
int len=(int)str.length();
In this example, it is okay because we know the size of the string will fit in a int, but if you were getting a string from an outside source, this could be a problem. A std::string using an unsigned integer type to store the size of the string and that means you can have a string who's size is larger then what can fit in an int. If that were to happen, then code is not going to work correctly.
With your code you a avoid all that, which is great, but you also leave some performance on the table. In theory your code of
for(it=str1.rbegin(); it!= str1.rend(); it++)
{
str2.push_back(*it);
}
is going to cause str2 to have multiple buffer allocations and copies from the old buffer to the new buffer as it grows. This is a lot of extra work that you don't need to do since you already know how much space you need to allocate. Having
str2.reserve(str1.size() + 1);
before the loop pre-allocates all the space you need so you don't have those potential performance hits.
Then we come to the fact that both of your examples are using a second string. You don't need another string to check for a palindrome. What you can do is just check and see if the first and last characters are the same, and if they are move on to the first+1 and last-1 character and so on until you reach the middle or they don't match. You can do that using a construct like
bool is_palindrome = true;
for (auto start = str.begin(), end = str.end() - 1;
start < end && is_palindrome;
++start, --end)
{
if (*start != *end)
is_palindrom = false
}
if (is_palindrome)
std::cout << "palindrome\n";
else
std::cout << "not a pallindrome\n";
The simplest and most efficient way (no copying required) would be something like this:
inline bool is_palindrome(const std::string& u) {
return std::equal(u.begin(), std::next(u.begin(), u.length() / 2), u.rbegin());
}
I would say that both are almost the same, but as mentioned in the comments, the line:
str2.push_back(*it);
Is actually very inefficient, since std::string may copy the existing string to a new location in the memory, and then append the next char to the string, which is wasteful.
But I am wondering, why to create the copy in the first place?
It is very simple to run both from start to end, and from end to start to check it out, meaning:
bool is_polindrom(const std::string& str)
{
for (std::size_t idx = 0, len = str.length(); idx < len / 2; ++idx)
{
if (str[idx] != str[len - 1 - idx])
{
return false;
}
}
return true;
}
Running the code with:
int main()
{
const std::string right1 = "MADAM";
const std::string right2 = "MAAM";
const std::string wrong1 = "MADAAM";
const std::string wrong2 = "MEDAM";
std::cout << "MADAM result is: " << is_polindrom(right1) << std::endl;
std::cout << "MAAM result is: " << is_polindrom(right2) << std::endl;
std::cout << "MADAAM result is: " << is_polindrom(wrong1) << std::endl;
std::cout << "MEDAM result is: " << is_polindrom(wrong2) << std::endl;
}
Will yield:
MADAM result is: 1
MAAM result is: 1
MADAAM result is: 0
MEDAM result is: 0
You don't need extra memory in this case, since it is possible to iterate over a string from the end to the beginning, and you need to run on it exactly once (and notice that I stop when idx >= len / 2 since you don't really need to check each letter twice!).
I'm working on this code that takes a numeric string and fills an array with each "digit" of the string. The issue I'm having is trying to convert an integer to a string. I tried using to_string to no avail.
Here is the code (note this is pulled from a larger program with other functions):
#include <cstdlib>
#include <stdlib.h>
#include <iostream>
#include <time.h>
#include <typeinfo>
int fillarr(int &length) {
int arr[length];
string test = "10010"; //test is an example of a numeric string
int x = 25 + ( std::rand() % ( 10000 - 100 + 1 ) );
std::string xstr = std::to_string(x); //unable to resolve identifier to_string
cout << xstr << endl;
cout << typeid (xstr).name() << endl; //just used to verify type change
length = test.length(); //using var test to play with the function
int size = (int) length;
for (unsigned int i = 0; i < test.size(); i++) {
char c = test[i];
cout << c << endl;
arr[int(i)] = atoi(&c);
}
return *arr;
}
How can I convert int x to a string? I have this error: unable to resolve identifier to_string.
As mentioned by user 4581301, you need an #include <string> to use string functions.
The following, though is wrong:
arr[int(i)] = atoi(&c);
The atoi() will possibly crash because c by itself is not a string and that mean there will be no null terminator.
You would have to use a buffer of 2 characters and make sure the second one is '\0'. Something like that:
char buf[2];
buf[1] = '\0';
for(...)
{
buf[0] = test[i];
...
}
That being said, if your string is decimal (which is what std::to_string() generates) then you do not need atoi(). Instead you can calculate the digit value using a subtraction (much faster):
arr[int(i)] = c - '0';
Okay I modified my code a bit per suggestion from everyone and ended up handling the conversion like this:
string String = static_cast<ostringstream*>( &(ostringstream() << x) )->str();
cout << String << endl;
I am passing a string to my function, and the function is supposed to use that string to put individual chars in a stack. Then the stack is supposed to spit it back out (Since it's a stack it should be reversed). For example if my string is hello, it should print "olleh". But instead I'm getting ooooo. I think it has something to do with the fact that I'm setting ch equal to a different character every time but I'm not sure how to input those character in a different way.
void Stack::function2reverse(string myString) {
int countItIt = 0;
int sizeOfString = myString.size();
char Ch ;
for (int i= 0; i< sizeOfString; x++)
{
Ch = myString[x];
stack.push(Ch);
countIt ++;
}
while (countIt != 0)
{
cout << Ch;
stack.pop();
countIt --;
}
}
cout << Ch; - you print the same character every time (the last one entered, so 'o').
Instead, print the top character in the stack: std::cout << stack.top().
std::stack keeps track of its own size, so you don't need to worry about that either. Then you can replace your print loop with:
while (!stack.empty()) {
std::cout << stack.top();
stack.pop();
}
And of course, the Standard Library provides a std::reverse function anyway, so if this was not just an exercise in learning about std::stack, you could use that (and I can think of several other things to do as well, depending on exactly what you are trying to achieve):
std::string s = "hello";
std::reverse(std::begin(s), std::end(s));
// s now contains "olleh"
You may also want to read up on why using namespace std; is a bad practice.
I am to use "C-strings, not C++ string objects" per teacher to read a paragraph from an input file and count characters, words, sentences, and the number of to-be verbs (to, be, am, are, is, was, were). My current program clears compiler but my character count and words count both off by two. My code to count sentences stops after counting one sentence. Any help as to my errors is appreciated. Still working to debug. My teacher basically told the class what strtok did and then literally told us to figure it out from there. Not asking for you to do it for me -- just tips/hints to get back in the right direction. Happy Easter.
#include <iostream>
#include <cstring>
#include <fstream>
#include <cstdlib>
using namespace std;
const int MAX_FILENAME = 256;
//Longest word length = 15 chars
const int MAX_WORD_CHARS = 16;
//Longest word in paragraph = 200 words
const int WORDS_MAX = 200;
//Max amount of chars in paragraph = 15*200
const int MAX_PARAGRAPH_CHARS = 3000;
//Max chars in a sentence
const int MAX_SENTENCE_CHARS = 200;
const int MAX_SENTENCES = 25;
const int NUM_TO_BE_VERBS = 5;
void readParagraph( ifstream& input, char [] );
int countWords( char [], char tp[][MAX_WORD_CHARS] );
int countSentences( char [] );
int main()
{
int i;
int words, sentences, average;
char filename[MAX_FILENAME];
ifstream input;
//Holds paragraph characters
char p[MAX_PARAGRAPH_CHARS];
const char TO_BE_VERBS[NUM_TO_BE_VERBS][MAX_WORD_CHARS] = { "am", "are", "is", "was", "were" };
const char BE[] = "be";
const char TO[] = "to";
char tp[WORDS_MAX][MAX_WORD_CHARS];
//Prompt user input file name
cout << "Enter input file name: ";
cin.get( filename, 256 );
//Open input file
input.open( filename );
//Check input file exists
if ( input.fail() )
{
cout << "Input file " << filename << " does not exist." << endl;
exit(1);
}
//Reads paragraph into array
readParagraph( input, p );
countWords( p, tp );
countSentences( p );
return(0);
}
void readParagraph( ifstream& input, char p[] )
{
int count = 0;
while ( input.get( p[count]) && (count < MAX_PARAGRAPH_CHARS) )
{
count++;
}
p[count - 1] = '\0';
cout << "Number of characters: " << count << endl;
}
int countWords( char p[], char tp[][MAX_WORD_CHARS] )
{
int i = 0;
char* cPtr;
cPtr = strtok( p, " " );
while ( cPtr != NULL )
{
strcpy( tp[i], cPtr );
i++;
cPtr = strtok( NULL, " " );
}
cout << "Number of Words: " << i << endl;
return(i);
}
int countSentences( char p[] )
{
int j = 0;
char* Ptr;
char sent[25];
Ptr = strtok( p, ".!?" );
while ( Ptr != NULL )
{
strcpy( sent, Ptr );
j++;
Ptr = strtok( NULL, ".!?" );
}
cout << "Number of sentences: " << j << endl;
return(j);
}
There are a number of problems with the code and a problem with the assignment. First, as has been mentioned, this is not the way that a professional programmer would approach this task, so shame on your instructor for not teaching students real-world use of C++. That said, there is still plenty to be learned with such an assignment. Here are some issues for you to consider and correct.
Unused variables
Unused variables are very often a sign of poor quality code. They clutter up the code, making it hard to understand and add nothing of value. Your compiler can help you find these kinds of problems if you ask it to be particularly picky. I used g++ to compile your code and used the following command line:
g++ -Wall -Wextra -pedantic --std=c++11 -o stringcount stringcount.cpp
It helpfully pointed out that i, words, sentences, average, TO_BE_VERBS, BE and TO are unused.
using namespace std;
Incorporating using namespace std; into every program you write is a bad habit that you should avoid. If your instructor is advising that you use this construct, be aware that you're going to have to unlearn this kind of bad habit if you ever decide to write code other than for that class.
Reading paragraphs
Your readParagraph routine includes this loop:
while ( input.get( p[count]) && (count < MAX_PARAGRAPH_CHARS) )
but consider that this will only stop if it reaches the end of the file or exceeds the MAX_PARAGRAPH_CHARS which is not the usual definition of a paragraph. Instead, you probably want to use some other indicator, such as a blank line, to indicate a paragraph has ended.
Separation of output from core logic
Many of your routines print as well as doing some kind of calculation. While this is a reasonable way to help troubleshoot non-working code, generally it's better to separate the calculation (as with readParagraph) from printing. That means that what would likely make more sense is to have readParagraph return an unsigned that represents the number of characters in the paragraph and not print anything. If something needs to be printed, print it from some other routine or from main.
Error handling
Look at your readParagraph function:
void readParagraph( std::ifstream& input, char p[] )
{
int count = 0;
while ( input.get( p[count]) && (count < MAX_PARAGRAPH_CHARS) )
{
count++;
}
p[count - 1] = '\0';
std::cout << "Number of characters: " << count << std::endl;
}
Is that \0 character really going in the right place? Where does it get written if there are no characters read?
Think carefully about the problem
Can you count the words in this sentence? I count eight. Your program might not because your countWords function includes this line:
cPtr = strtok( p, " " );
Note that words might end with a period or question mark and not necessarily with a space character. The strtok function can actually look for multiple delmiters within the same call, so you may wish to adjust that call.
Creating extra work
The countWords function carefully copies each discovered word into the tp array, but the tp array is then never used. If you don't use it, don't create it. It will make your program more durable and easier to troubleshoot as well as being slightly faster. The same is true of the mysterious sent variable in your countSentences function.
return is not a function
Don't write return(j); because return is not a function. Instead, just write return j; which is more idiomatic C and C++ style.
Magic numbers
You've used a number of const int declarations for constants in your program, which is a good thing. However, there are still some mysterious and unexplained "magic numbers" that are embedded in the program such as the 256 in this line:
std::cin.get( filename, 256 );
(Note that I've added std:: in front to show what it would look like when you omit the using namespace std; from your program.)
Here's how to debug code like this (which is related to, but not the same as, the way to write code well and avoid this predicament in the first place).
Step 1: pick an observed problem and lock it down.
The character count is off. Given the text "red apple", the code reports Number of characters: 10
Step 2: Find the relevant code and strip it down as far as you can, while still displaying the behavior.
void readParagraph( ifstream& input, char p[] )
{
int count = 0;
// while ( input.get( p[count]) && (count < MAX_PARAGRAPH_CHARS) )
char c;
while ( input.get(c))
{
count++;
}
// p[count - 1] = '\0';
cout << "Number of characters: " << count << endl;
}
Step 3: put in some diagnostic output statements so as to see what's going on (or, for more advanced students, run the whole thing in a debugger).
void readParagraph( ifstream& input, char p[] )
{
int count = 0;
char c;
while(input.get(c))
{
count++;
cout << count << ": " << c << endl;
}
cout << "Number of characters: " << count << endl;
}
The output is:
1: r
2: e
3: d
4:
5: a
6: p
7: p
8: l
9: e
10:
The code is counting spaces and carriage returns as characters.
Step 4: fix it.
How you deal with this depends on what you want it to count.
Go back to Step 1, repeat until you're satisfied.
I'm working on a small little thing here for school. After hours of researching, and a ton of errors and logic reworking I've almost completed my little program here.
I'm trying to take user input, store it into the string, get a character array from the string ( dont ask why, I just have to put this into a character array ), then get the reversed order of the phrase that the user entered. Here is my code:
#include "stdafx.h"
#include <iostream>
#include <String>
#include <cstring>
using namespace std;
using namespace System;
#pragma hdrstop
char* getCharArray(string);
string reversePhrase( int, char* );
void main(void)
{
string sPhrase = "";
int sSize = 0;
string sReversed = "";
char* cPhrase = NULL;
cout << "Welcome to the You Type It & We'll Reverse it! [Version 1.0] " << endl;
cout << "This program will reverse your phrase, and count how many characters are in it!" << endl;
cout << "To begin just enter a phrase." << endl;
cout << "Enter a phrase: ";
getline( cin, sPhrase);
sSize = sPhrase.length();
cout << endl;
cPhrase = getCharArray(sPhrase);
sReversed = reversePhrase( sSize, cPhrase );
cout << sReversed;
system("pause");
}
string reversePhrase(int size , char* cPhrase)
{
string sReversed = "";
int place = size;
for ( int i = 0; i < size ; i ++ )
{
sReversed.append(1, cPhrase[place]);
cout << "Current string: " << sReversed << endl;
cout << "Current character: " << cPhrase[place] << endl;
place--;
}
return sReversed;
}
char* getCharArray(string sPhrase)
{
int size = 1;
size = sPhrase.length();
char* cArray = NULL;
cArray = new char[size];
for (int i = 0 ; i < size ; i++)
{
cArray[size] = sPhrase.at(i);
}
return cArray;
}
When I type in "ownage" into the program, this is what I get returned:
It is almost like my Character Array is getting garbage collected before it can use all of the characters. This is probably an easy fix but, I just don't see how I can get around this one.
Try rewriting getCharArray like this
char* getCharArray(string sPhrase)
{
int size = 1;
size = sPhrase.length();
char* cArray = NULL;
cArray = new char[size+1]; // NOTE
for (int i = 0 ; i < size ; i++)
{
cArray[i] = sPhrase.at(i); // NOTE
}
}
cArray[size]=0; // NOTE
return cArray;
}
Note that the assignment in the loop now uses the index variable. Also, you need to allocate one extra char in the array to set the null terminator for the string and then you need to set it at the end.
You'll also need to think about deallocating the array at some point
The bug is in this line:
cArray[size] = sPhrase.at(i);
That size should be your loop index.
You should probably look at using std::string more, and not poke around with character arrays when there's no need to.
Why use a char array at all? It's not only useless – it complicates the code substantially (the usage of your function is more difficult, and you've forgotten to free the memory allocated by new!). Why not just have the following function:
string reverse(string const& input);
(Passing the argument by const reference instead of by value saves you a copy!)
In fact, implementing the function only takes a single line using the features of the string class (one of its constructors takes two iterators):
string reverse(string const& input) {
return string(input.rbegin(), input.rend());
}
reversePhrase is also not correct. Try something like this:
string reversePhrase(int size , char* cPhrase)
{
string sReversed = "";
sReversed.resize(size);
int place = size - 1;
for ( int i = 0; i < size ; i ++ )
{
sReversed [i] = cPhrase[place];
cout << "Current string: " << sReversed << endl;
cout << "Current character: " << cPhrase[place] << endl;
place--;
}
return sReversed;
}
First, start the array with -1. After that, use a for loop with -1 and increment inside the loop. Then, you can get the first element of the array.