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 '('.
Related
For my homework, part of what I need to do is take a phrase from the user, and from there take only the letters in the phrase, ignoring numbers, spaces, and special characters. Once I find letters in the string, I need to store them into a separate variable. However, I can't get that variable to store anything outside of the if statement that looks for letters.
#include<iostream>
#include<string>
using namespace std;
int main()
{
string line, temp;
cout << "Enter phrase to check: ";
getline(cin, line);
for(int i = 0; i < line.size(); i++)
{
if((line[i] > 64 && line[i] < 91) || (line[i] > 96 && line[i] < 123))
{
temp[i] = line[i];
}
}
cout << temp;
return 0;
}
When I run the program, temp outputs nothing. But I know the if statement is correctly finding letters, from making it print line[i] inside the if statement.
your temp variable is an empty string. temp[x] is telling the compiler to change the x-th character of that string(which doesn't make any sense, as the string doesn't have any characters!). You're lucky(or unlucky) that you aren't getting any Segmentation faults(crashes).
Just use the += operator:
temp += line[i];
Try
temp.push_back(line[i]);
It will work.
The way you're currently doing it (temp[i] = line[i];) means that each non special character in line will be placed at the same index in temp. This should usually fail since temp (a string) does not resize on indexing.
For changing the size of a string, there exists a function called string::push_back as detailed here.
Instead of indexing using temp[i], you would instead use temp.push_back(line[i]);
This function allows the string to resize itself to accommodate the new char if need be and won't throw a segmentation fault.
NB: std::string::push_back is designed to append a single char to a string. There exist multiple other ways of doing this, including Nikita Demodov's answer which shows the use of the += operator which is much more lenient and will allow appending of other strings etc. push_back is most common to the std::vector where it is used to append single items to the list.
I'm trying to read a column of names from a text file into a two dimensional array, the names vary in length but are at max 8 letters long, and there are 10 of them. Here are my two for loops to read the names and then to print them.
for (int i = 0; i != 10; i++) {
for (int j = 0; j != 8; j++) {
infile >> textfileinfo[i][j];
}
}
and then to print the names out I have this loop.
for (int i = 0; i != 10; i++) {
for (int j = 0; j != 8; j++) {
cout << textfileinfo[i][j];
}
cout << " " << endl;
}
Here is the list of names:
Victor
Eve
Juliet
Hector
Danielle
Romeo
Oscar
June
Ares
Dannae
What ends up happening is it will read the names out with 8 characters regardless, taking the extra characters from the next name, so Victor turns into VictorEv, then eJulietH and so on. How do I get it to start on the next row once the end of Victor is reached, and then move on to Eve etc. I am not allowed to use pointers either. Any help is appreciated! Thanks.
Seems the obvious thing is to use getline. getline is designed to read a single line of text.
for (int i = 0; i != 10; i++) {
infile.getline(textfileinfo[i], 9);
}
Note that the second argument to getline is 9 because you need an extra character to store the nul terminator that ends every C style string. This also mean that your 2D array need to be declared with at least 9 characters in each row. Slightly safer code would be to make this explicit
infile.getline(textfileinfo[i], sizeof textfileinfo[i]);
If for some reason you aren't allowed to use getline then you going to have to modify your inner loop to detect and deal with the '\n' character that terminates every line.
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.
So I want to create 1000 words with a length of 5 random characters. In my main I have word[1000] but when I try to run this code, it gives me an error saying "Expression:string subscript out of range". I'm not sure why it does that because I thought string arrays were 2 dimensional? If anyone could tell me why my code is wrong I'd appreciate it. Thanks.
void createText(string w[], int seed) {
char ch;
srand(seed);
for(int i = 0; i < 1000; i++) {
for(int j = 0; j < 5; j++) {
ch = ('A' + rand() % 26);
w[i][j] = ch;
}
}
for(int i = 0; i < 1000; i++) {
cout << w[i];
}
cout << endl;
}
I suppose that the array w does not have 1000 elements, also remember that here you will get a copy of string w[]. Better would be passing a pointer to w (string* w), then You will have very clearly what is wrong. remember also that cout writes the string out untill it reaches a '\0' character, this also might be the cause. Quick session with gdb will help:
gdb program
...
run
bt full
should pinpoint Your problem. if it's some kind of IDE, learn how to debug in it. Valgrind or some other memcheck like visual leak detector or luke stackwalker will also show you some tips about bad initialization or unmentioned memory leaks.
If an array is bidimensional, you can't print its values like w[i]. You must print always keeping in mind that the array is bidimensional, which means that the output must be done like cout << w[i][j];
In addition, you're passing an array of strings as an argument, and what you're doing is add characters to every single position, which means that you won't actually have nothing but 1000 characters inserted into that string (because you actually added "one-char" strings), so you'll only put 200 words with a length of 5 characters each one. Insert strings directly, and you'll get your 1000 words, but first find a way to build strings with random characters.
Something like:
for(conditions){
for(conditions){
build a word
}
array[x][y] = string
}
I guess it is similar to what you intended to do
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;
}