I'm trying to take user input from the console without using the c++ standard library classes. Here's my code
while(true){
std::string line = " ";
while (getline(std::cin, line)) {
std::string arr[100];
int i = 0, len = 0;
for (int j=0; j < line.length(); j++) {
if(line[j] ==' ' || line[j] == '\n'){
std::string word = line.substr(i, j);
arr[len] = word;
len++;
i = j;
}
}
for (int k = 0; k <len ; ++k) {
std::cout<<arr[k]<<std::endl;
}
}
//break;
}
The idea is to identify each word and store it in an array. However this program only identifies the first word. Any idea, what am I doing wrong here.
In reading this, it would seem your problem lies on the line std::string word = line.substr(i, j);. You have to understand that substr's arguments are not "from i to j", but "from i, j characters". Read the documentation. :)
I haven't tested this, so it might not be perfect, but the principle is there.
while(true){
std::string line = " ";
while (getline(std::cin, line)) {
std::string arr[100];
int num_chars = 0, word = 0;
for (int i=0; i < line.length(); i++) {
/*We increment i, so every non-space character is one we
* will include in the word.*/
num_chars++;
if(line[i] ==' ' || line[i] == '\n'){
/*We want all characters from i to i+num_chars, that is
* we want all characters from j, forward i indices.
*/
std::string word = line.substr(i, num_chars);
arr[word] = word;
word++;
//We reset i here, in prep for parsing the next word.
i = 0;
}
}
for (int k = 0; k <len ; ++k) {
std::cout<<arr[k]<<std::endl;
}
}
//break;
}
Two other considerations:
1) Beware the single-letter variables, as it makes it far harder to read your code later. i is standard for a loop iterator or index, with j being the next when you're nesting for loops. However, i is not suitable for the "length of the word". Similarly, len is not suitable for the index of the word being stored. I changed the variables in the code to make it easier to read.
2) I would seriously consider revisiting your loop structure. while is common and very useful, but it is also highly prone to infinite loops. In fact, while(true) IS an infinite loop, so if you don't reach break for whatever reason, you'll wind up with some serious issues.
--
I would also agree that, if you want to avoid "STL" (and, actually std:: and stl are commonly confused, but are not the same thing...so let's say you want to avoid std), you'll want to avoid std::string and std::cin. Use C-strings and scanf/printf instead, as Nidhoegger suggested. It is more efficient than the std options, but it also more prone to the errors and "undefined behaviors" characteristic to C. It would take more effort, but would yield a more efficient result if you do it right.
While we're at it, I do NOT recommend std::stringstream unless your other tools cannot do the job well. That class has serious performance and efficiency problems, which are well-documented. I only recommend using it in cases where writing your own code with std::string and such would be either too laborious or have a high probability of being inefficient. This is NOT one of those cases.
Related
Is there a propper way in an isstrinstream to skip/ ignore the next, or even the next n words?
The possibility to read n times a variable seems to work, but is very clunky:
for (int i = 0; i < n; ++i)
{
std::string tmp;
some_stream >> tmp;
}
std::istream::ignore doesn't seem to do the job, as only n letters are skipped.
It's clunky because it's not common enough to have gotten the appropriate attention to get a standard algorithm in place.
{
std::string tmp;
for(size_t i = 0; i < number_of_words_to_skip; ++i) some_stream >> tmp;
}
You can make it fancier by creating a null receiver:
std::copy_n(std::istream_iterator<std::string>(some_stream),
number_of_words_to_skip,
std::back_inserter(instance_of_something_that_does_not_do_anything));
Since the std library is void of such a receiver and adding it for this one case is likely to cause more confusion than your original solution., I'd say that the idiomatic approach, Today, is just to make a loop like above.
How do i make this?
image of my homework
note: Batasan means limitaion and Contoh means example
So, my professor wants me to do make output the same size horizontal and vertically in pattern shown in the image
I dont know what to do, but the best i can make is this:
#include<bits/stdc++.h>
using namespace std;
int main(){
int n;
const char * array1[4];
const char * array2[4];
array1[0] = "O", array1[1] = ">", array1[2] = "X", array1[3] = "<";
array2[0] = "v", array2[1] = "/", array2[2] = "^", array2[3] = "\\";
cin>>n;
for(int i = 1; i <= n; i++){
if (i%2 != 0){
for(int j = 0; j <=n; j++){
cout << array1[j];
}
cout<<"\n";
} else if (i%2 != 0) {
for(int j = 0; j <=n; j++){
cout << array2[j];
}
cout<<"\n";
}
return 0;
}
}
I dont know if array is necessary or not.
If you guys have any suggestion about my program feel free to give me some.
This is my first time asking in this web and im sorry if my post and english are terrible
Thanks in advance:)
We are here to help.
I will first show you the problems in your code and then make a proposal on how to make it better.
So, let us first check your code:
#include<bits/stdc++.h> is a non C++ compliant compiler extension. It should never be used. On my machine, it does not compile.
using namespace std; should not be used. It is better to always use full qualified names. This will avoid name clashes from different scopes or namespaces
Variables should have meaningful names. One character variables are in most cases not that good
All variables should be initialized during definition
C-Style arrays should not be used in C++. Always use a specialized STL container like std::vector or std::array
In C++ we use std::string for strings and not char[] or char *
Array indices in C/C++ start with 0. If you use <= in the end condition of a for loop, you will access an element one past the end. This is a severe out of bound error. You do that in you for loop with the 'j'
There is anyway a severe out of bound bug here. You access array[j] and j might be 4 or bigger. That is a bug and must be corrected. You can simply do a modulo devision % by 4. Then you do never exceed the 4. it will then always be 0,1,2,3,0,1,2,3,0,1,2,3 . . .
You should write as much as possible comments
If we correct all this findings, then we could come up with:
#include <array>
#include <iostream>
constexpr size_t NumberOfLinePatterns = 2;
constexpr size_t NumberOfElementsPerLinePattern = 4;
using Pattern = std::array<std::array<char, NumberOfElementsPerLinePattern>, NumberOfLinePatterns>;
// If you do not yet know the std::array. Then uncomment the following and
// remove on opening and closing curly brace in the initialization below
// using Pattern = char[NumberOfLinePatterns][NumberOfElementsPerLinePattern];
Pattern pattern{{
{'O','>','X','<'},
{'v','/','^','\\'}
}};
int main() {
// Get number of rows and columns to print
unsigned int numberOfElements{}; std::cin >> numberOfElements;
// Now, for all rows and columns
for (unsigned int row{}; row < numberOfElements; ++row) {
for (unsigned int column{}; column < numberOfElements; ++column) {
// Print the selected character
std::cout << pattern[row % NumberOfLinePatterns][column % NumberOfElementsPerLinePattern];
}
std::cout << '\n';
}
return 0;
}
I have an input string which I will use to build an output string. The output string is much the same as the input string but has some slight changes along the way, depending on if we hit a certain characters in the input. The code looks like this
outputTree(std::ostream& o, const char* input) {
size_t len = strlen(input);
int indent = 0;
string output;
for(size_t i = 0; i < input_len; i++) {
if(input[i] == '(') {
indent++;
output.append(1,'\n');
for(int j = 0; j < indent; j++) {
output.append(" ");
}
}
if(input[i] == ')') {
output.append(1,'\n');
for(int j = 0; j < indent; j++) {
output.append(" ");
}
indent--;
}
output.append(1,input[i]);
}
o << output << endl;
}
While this works, doing it character by character is pretty slow. Can anyone recommend some better approaches to this (pref with standard features, ie no boost)?
I am not sure what "pretty slow" means and how much improvement you expect. The algorithm you have is an efficient O(N) algorithm. The only thing you can do is trying to optimise individual operations, but then again I am not sure what kind of optimisations is the compiler doing.
One thing you could do is get rid of the inner loops that write the indentation string as many times is needed by the level of indentation (your indent variable). So instead of writing a newline and then execute the for loop, you can perform just one append that has the newline concatenated with the right amount of indentation. If you can set a limit to the depth of your indentation (say 10 levels deep) you can then create an array that has all 10 different indentation strings (all strings starting with a newline).
Another thing you can try is to use std::string::find to find the occurrences of '(' and ')' and do string copies up to these markers, instead of copying character by character.
Finally, note that your code might not behave as you want it, if the input is not as expected. Notice what will happen if you first encounter a ')' without having a corresponding '('.
I am using two dynamic arrays to read from a file. They are to keep track of each word and the amount of times it appears. If it has already appeared, I must keep track in one array and not add it into the other array since it already exists. However, I am getting blank spaces in my array when I meet a duplicate. I think its because my pointer continues to advance, but really it shouldn't. I do not know how to combat this. The only way I have was to use a continue; when I print out the results if the array content = ""; if (*(words + i) == "") continue;. This basically ignores those blanks in the array. But I think that is messy. I just want to figure out how to move the pointer back in this method. words and frequency are my dynamic arrays.
I would like guidance in what my problem is, rather than solutions.
I have now changed my outer loop to be a while loop, and only increment when I have found the word. Thank you WhozCraig and poljpocket.
Now this occurs.
Instead of incrementing your loop variable [i] every loop, you need to only increment it when a NEW word is found [i.e. not one already in the words array].
Also, you're wasting time in your inner loop by looping through your entire words array, since words will only exist up to index i.
int idx = 0;
while (file >> hold && idx < count) {
if (!valid_word(hold)) {
continue;
}
// You don't need to check past idx because you
// only have <idx> words so far.
for (int i = 0; i < idx; i++) {
if (toLower(words[i]) == toLower(hold)) {
frequency[i]++;
isFound = true;
break;
}
}
if (!isFound) {
words[idx] = hold;
frequency[idx] = 1;
idx++;
}
isFound = false;
}
First, to address your code, this is what it should probably look like. Note how we only increment i as we add words, and we only ever scan the words we've already added for duplicates. Note also how the first pass will skip the j-loop entirely and simply insert the first word with a frequency of 1.
void addWords(const std::string& fname, int count, string *words, int *frequency)
{
std::ifstream file(fname);
std::string hold;
int i = 0;
while (i < count && (file >> hold))
{
int j = 0;
for (; j<i; ++j)
{
if (toLower(words[j]) == toLower(hold))
{
// found a duplicate at j
++frequency[j];
break;
}
}
if (j == i)
{
// didn't find a duplicate
words[i] = hold;
frequency[i] = 1;
++i;
}
}
}
Second, to really address your code, this is what it should actually look like:
#include <iostream>
#include <fstream>
#include <map>
#include <string>
//
// Your implementation of toLower() goes here.
//
typedef std::map<std::string, unsigned int> WordMap;
WordMap addWords(const std::string& fname)
{
WordMap words;
std::ifstream inf(fname);
std::string word;
while (inf >> word)
++words[toLower(word)];
return words;
}
If it isn't obvious by now how a std::map<> makes this task easier, it never will be.
check out SEEK_CUR(). If you want to set the cursor back
The problem is a logical one, consider several situations:
Your algorithm does not find the current word. It is inserted at position i of your arrays.
Your algorithm does find the word. The frequency of the word is incremented along with i, which leaves you with blank entries in your arrays whenever there's a word which is already present.
To conclude, 1 works as expected but 2 doesn't.
My advice is that you don't rely on for loops to traverse the string but use a "get-next-until-end" approach which uses a while loop. With this, you can track your next insertion point and thus get rid of the blank entries.
int currentCount = 0;
while (file)
{
// your inner for loop
if (!found)
{
*(words + currentCount) = hold;
*(frequency + currentCount) = 1;
currentCount++;
}
}
Why not use a std::map?
void collect( std::string name, std::map<std::string,int> & freq ){
std::ifstream file;
file.open(name.c_str(), std::ifstream::in );
std::string word;
while( true ){
file >> word; // add toLower
if( file.eof() ) break;
freq[word]++;
}
file.close();
}
The problem with your solution is the use of count in the inner loop where you look for duplicates. You'll need another variable, say nocc, initially 0, used as limit in the inner loop and incremented whenever you add another word that hasn't been seen yet.
I'm attempting to write alil function here which basically reads from a string. It reads every three characters and evaluates it using the pre-condition (if statement). If conditions are met, it would replace those three letters with new three letters. Then it would output the new string.
I tried writing the code but cant seem to get the logic right. the program runs but it doesn't print out anything. Dont mind the function name and the inaccuracy. I'm just doing a sample function to test this out.
string amino_acids(string line)
{
string acid;
string acids;
string newline;
for( int i= 0; i < line.length(); i++)
{
acid = line[i];
}
for (int i = 0; i < 3; i++)
{
acids = acid[i];
if(acids == "GUU")
{
acids = "ZAP";
}
newline = acids;
}
cout << "Acids: " <<newline <<endl;
return newline;
}
for( int i= 0; i < line.length(); i++)
acid = line[i];
Say line contains "abcd", this loop is going to do:
acid = 'a';
acid = 'b';
acid = 'c';
acid = 'd';
Only the last assignment has any lasting affect. If you need to actually get three characters from line into acid - you probably want to use += to add characters into acid, rather than =. But, if you loop over all of line like this, you'll end up doing acid = line;. I assume you want something more like acid = line.substr(0, 3)?
for (int i = 0; i < 3; i++)
{
acids = acid[i];
This is going to crash. acid is definitely a single character string, and you're indexing into acid[1] and acid[2] on the 2nd and 3rd iterations. While you're learning C++, you should probably use .at(i) which will throw an exception when you attempt to use an invalid index - you can catch the exception and at least have some indication of the problem. As is, it's undefined behaviour.
To use at, you need a try / catch block... the basic form is:
int main()
try
{
...your code in here...
some_string.at(i);
}
catch (const std::exception& e)
{
std::cerr << "caught exception: " << e.what() << '\n';
}
More generally, try putting some std::cout statements throughout your code so you know what values your variables actually have... you would easily have seen that they weren't what you expected. Alternatively, use an interactive debugger and watch the affect of each statement's execution.
Indexing a std::string with the [] operator yields a char, for which there just happens to be an overloaded operator= for strings.
Even if you were looping as I believe you intended (which, as the comments on the question mention, you probably aren't) because acids (which takes the value of a single character) will never be equal to the three character string you're comparing it to. Thus, no replacements will be performed.
To do what you want, try something like this:
for (int i = 0; i + 3 < line.length(); i += 3) // counting by 3 until end of line
{
if (line.substr(i, 3) == "GUU") // if the substring matches
{
line.assign("ZAP", i, 3); // overwrite it with new substring
}
}
return line;
Reading from your description, you want something like so
//note below does not compile, its just psuedo-code
string amino_acid(const string& sequence){
string result = sequence; //make copy of original sequence
For i = 0 to sequence.length - 3
string next3Seq = sequence(i,3); //grab next 3 character from current index
If next3Seq == 'GUU' //if the next next three sequence is 'GUU'
then result.replace(i,3,'ZAP'); //replace 'GUU' with 'ZAP'
EndIf
EndFor
return result;
}
You can use that as a start to code. Good Luck.
According to my understanding of your question. I have written some code. Please look below
string acids;
string newLine;
int limit=1;
for(int i=0;i<line.length();i++)
{
acids=acids+line[i];
if(limit==3)//Every 3 characters
{
if(acids == "GUU")
{
acids = "ZAP";
}
limit=1;
acids=""
newline=newline+acids;
}
limit++;
return newline;
}