How to read string from stdin until meet blank lines - c++

Consider a simple program. It must take string from stdin and save to variable.
It is not stated how many lines of input will be taken, but program must terminate if meet newline.
For example:
stdin:
abc
abs
aksn
sjja
\n
I tried but it doesn't work. Here is my code:
#include <iostream>
#include <cstring>
#include <cstdio>
using namespace std;
// Constant
#define max 100000
struct chuoi
{
char word[10];
};
chuoi a[max];
void readStr()
{
int i=0;
while ( fgets(a[i].word, 10,stdin) != NULL)
{
if (a[i].word[0] == ' ') break;
a[i].word[strlen(a[i].word)-1] = '\0'; //replaced \n by \0
i++;
}
//length = i;
}
int main()
{
readStr();
return 0;
}
So, how to solve this problem?

One alternative here is to use std::getline to get each line. If the line is empty, or the input fails, then exit the loop.
void readStr()
{
std::string str;
while ( std::getline(std::cin, str) && str.length() )
{
// use the string...
}
}
Adding the std::getline and use of std::vector to your sample code, and keeping with the spirit of your original sample;
#include <string>
#include <iostream>
#include <vector>
const std::size_t Max = 100000;
struct chuoi
{
explicit chuoi(std::string const& str) : word(str)
{
}
std::string word;
};
void readStr(std::vector<chuoi>& a)
{
std::string str;
while ( std::getline(std::cin, str) && str.length() )
{
a.push_back(chuoi(str));
}
}
void writeStr(std::vector<chuoi> const& a)
{
for (auto i = a.begin(); i != a.end(); ++i) {
std::cout << i->word << std::endl;
}
}
int main()
{
std::vector<chuoi> a;
a.reserve(Max);
readStr(a);
writeStr(a);
return 0;
}
To solve you immediate problem, minimal changes in the code can be made as follows;
void readStr()
{
int i = 0;
while ( fgets(a[i].word, 10, stdin) != NULL)
{
a[i].word[strlen(a[i].word) - 1] = '\0'; // transform the end of line character to NULL
if (strlen(a[i].word) == 0) {
break;
}
i++;
}
}
If the standard input will always be used (stdin), the gets function can also be used;
while ( gets(a[i].word) != NULL)
{
if (strlen(a[i].word) == 0) {
break;
}
i++;
}
Notes;
fgets reads until the "enter" key on the stdin but includes the new line character
gets also reads until the return, but excludes the new line character
Both functions NULL terminate the input
Be careful of the form of gets it does not check for buffer overflow conditions

I would do something like this:
#include <string>
#include <iostream>
int main()
{
std::string line; // will contain each line of input
// Stop when line is empty or when terminal input has an error
while(std::getline(std::cin, line) && !line.empty())
{
// do stuff with line
}
}

Related

A function uses a different amount of memory when I call it with the same conditions the second time

I have a problem with C++ and memory. Here's the pseudocode:
main.cpp
#include <iostream>
#include "seq.h"
int main(int argc, char *argv[]) {
SnpSite snp_site("/mnt/c/Users/manht/Downloads/s_typhi_wong_holt.aln.gz");
snp_site.test(); // run the first time
snp_site.test(); // run the second time
}
seq.h
#include "file_handler.h"
#include <stdio.h>
class SnpSite {
private:
string inputfile;
FileHandler fh;
public:
SnpSite(char* _inputfile);
int is_unknown(char base);
void test();
};
seq.cpp
#include "seq.h"
SnpSite::SnpSite(char* _inputfile) {
fh = FileHandler();
inputfile = _inputfile;
}
void SnpSite::test() {
string sample_name, seq;
this->fh.open(this->inputfile.c_str());
this->fh.assign_next_sample_to(&sample_name, &seq);
this->fh.close();
}
file_handler.h
#ifndef SEQ_H_
#include <zlib.h>
#include <utility>
#include <ctype.h>
#include "my_string.h"
#include <string>
using namespace std;
#define SEQ_H_
typedef bool (*match_func)(int c, int delimiter);
class FileHandler {
private:
gzFile file;
char buffer[2048]; // Static allocation for better performance.
int buffer_start, buffer_end;
bool eof;
void get_until(int delimiter, string *s);
public:
FileHandler();
FileHandler(int _buffer_size);
void open(const char* filename);
void close();
void assign_next_sample_to(string *name, string *seq);
int next_char();
bool is_eof();
};
#endif
file_handler.cpp
#include "file_handler.h"
FileHandler::FileHandler() {
buffer_start = -1;
buffer_end = -1;
eof = false;
}
void FileHandler::open(const char* filename) {
file = gzopen(filename, "r");
eof = false;
}
void FileHandler::close() {
gzclose(file);
}
int FileHandler::next_char() {
/* Read current character and increase cursor (buffer_start) by 1.*/
if (buffer_start >= buffer_end) {
buffer_end = gzread(file, buffer, 2048);
buffer_start = -1;
if (buffer_end == 0) eof = true;
}
return buffer[++buffer_start];
}
bool FileHandler::is_eof() {
return eof;
}
#define SEP_SPACE 0 // isspace(): \t, \n, \v, \f, \r
#define SEP_TAB 1 // isspace() && !' '
#define SEP_LINE 2 // line separator: "\n" (Unix) or "\r\n" (Windows)
#define SEP_MAX 2
// list of function to compare c and delimiter, need exactly 2 arguments.
bool match_space(int c, int delimter) {
return isspace(c);
}
bool match_tab(int c, int delimter) {
return isspace(c) && c != ' ';
}
bool match_newline(int c, int delimter) {
return c == '\n';
}
bool match_char(int c, int delimter) {
return c == delimter;
}
bool no_match(int c, int delimiter) {
return false;
}
// end list.
void FileHandler::get_until(int delimiter, string *s) {
/*
Read till delimiter and append bytes read to s.
When done cursor will be at the end of the line.
*/
match_func match; // function to check if a char match delimiter
switch (delimiter) {
case SEP_SPACE:
match = match_space;
break;
case SEP_TAB:
match = match_tab;
break;
case SEP_LINE:
match = match_newline;
break;
default:
if (delimiter > SEP_MAX) match = match_char;
else match = no_match;
}
// begin process
int i = buffer_start;
while (!match(buffer[i], delimiter)) {
if (buffer_start >= buffer_end) {
buffer_end = gzread(file, buffer, 2048);
buffer_start = 0;
i = 0;
if (buffer_end == 0) {
eof = true;
break;
}
}
while (!match(buffer[i], delimiter) && i < buffer_end) i++;
s->append((char*)(buffer + buffer_start), i - buffer_start);
buffer_start = i;
}
}
/*
Get next sample name and sequence, assign it to *name and *seq.
(Note: this function do not read quality score for QUAL file).
*/
void FileHandler::assign_next_sample_to(string *name, string *seq) {
/* Get next sample name and sequence, assign it to *name and *seq.*/
name->erase();
seq->erase();
int c;
while (!eof && (c = next_char()) != '>' && c != '#') {} // read until meet sample name
get_until(SEP_SPACE, name); // get sample name
while (!eof && (c = next_char()) != '>' && c != '#' && c != '+') {
if (c == '\n') continue;
get_until(SEP_LINE, seq); // read sequence
}
buffer_start--; // step back to the end of sequence
}
I don't use any dynamic allocation, and when I traced memory usage by PID in htop, I found something that I can't explain:
The first time I call test():
At the beginning of the function, my process uses 6168 KBytes.
At the end of the function, my process uses 13998 Kbytes.
The second time I call test():
At the beginning of the function, my process uses 6304 Kbytes.
At the end of the function, my process uses 21664 Kbytes.
The length of the seq variable is 4809037 and sample_name is 11 in both cases. I don't understand why memory usage is so different between them. Hope someone can find out and explain it to me, it helps me a lot. Thanks
This happens because of this line:
s->append((char*)(buffer + buffer_start), i - buffer_start);
Strings are dynamically allocated and every time the initial size is exceeded a new larger memory block is allocated. You can read more about this here: Chapter 4. Optimize String Use: A Case Study.

Is there way to reduce memory consumption in my c++ code?

I am new in c++ and I am trying to solve educational exercise in quiz platform, but in this platform I should use no more than 64 MB of memory. My code use more than 130 MB.
#include <sstream>
#include <string>
#include <fstream>
#include <iterator>
#include <vector>
#include <map>
using namespace std;
template<class Container>
void splitString(const std::string &basicString, Container &cont, char delim = ' ') {
std::stringstream ss(basicString);
std::string token;
while (std::getline(ss, token, delim)) {
cont.push_back(token);
}
}
int main() {
int target = 0;
int count = 0;
std::map<int, int> set;
string line;
ifstream fileR("input.txt");
std::vector<string> c;
if (fileR.is_open()) {
while (getline(fileR, line)) {
if (count == 0) {
target = std::stoi(line);
count++;
continue;
}
splitString(line, c);
for (auto &d : c) {
int key = std::stoi(d);
if (set.count(key)) {
set[key] += 1;
} else {
set[key] = 1;
}
}
c.clear();
}
fileR.clear();
fileR.close();
}
ofstream fileW;
fileW.open("output.txt");
bool found = false;
for (const auto &p : set) {
int d = target - p.first;
if (set.count(d)) {
if (p.first != d || set[d] > 1) {
fileW << 1;
found = true;
break;
}
}
}
if (!found) {
fileW << 0;
}
fileW.close();
return 0;
}
What I can add, remove or change for keep within the coveted 64 MB? I tried free memory manually but no effects. I am not sure that is possible to write more effective algorithm.
Your vector (c) is declared outside the loop and is not cleared every time you call split string. This means every time you pass in a string to split, your vector contains stuff from the previous run. Is this intentional? If it is not, then move your vector into the loop, before you call split string, or clear it in your split string function. If it is intentional please provide more info about what your code is supposed to do.

Replacing spaces in a string with increasing numbers

I need a program to take a string and replace spaces with increasing numbers.
#include <cstring>
#include <iostream>
#include <fstream>
#include <string>
using namespace std;
int main()
{
// Get the String
string str = "this essay needs each word to be numbered";
int num = 1;
string x = num;
int i = 0;
// read string character by character.
for (i < str.length(); ++i) {
// Changing the loaded character
// to a number if it's a space.
if (str[i] == ' ') {
str[i] = x;
++num
}
}
// testing outputs
cout << str << endl;
cout << num << endl;
ofstream file;
file.open ("numbered.txt");
file << str;
file.close();
return 0;
}
I had it at the point where it could replace spaces with a letter or symbol and save to a new file but when I tried to make it a number it stopped working. I would need it to say "this1essay2needs3each4word5to6be7numbered
For ease and clarity, change your approach.
Put the string into an istringstream
Extract each space-separated substring and place into an std::vector<string>
Feed the contents of the vector into a stringstream and
use std::to_string(num) to add the numbers between the substrings
e.g.:
std::string str = "this essay needs each word to be numbered";
int num = 1;
std::istringstream istr(str);
std::string temp;
std::vector<std::string> substrs;
while (istr >> temp)
{
substrs.push_back(temp);
}
std::stringstream ostr;
for (auto&& substr : substrs)
{
ostr << substr << std::to_string(num++);
}
Let's break the problem down into parts. We can make a SpaceReplacer object that does the replacement. It has an Output, which it can use as a function to output characters:
template<class Output>
struct SpaceReplacer {
Output output;
int num_spaces;
void input(char c) {
if(c == ' ') {
auto num_as_string = std::to_string(num_spaces);
num_spaces += 1;
for(char digit : num_as_string) {
output(digit);
}
}
else {
output(c);
}
}
};
Every time you input a character, it either outputs the character you input, or it outputs the digits of the number (if the character was a space).
We should write a helper function to make SpaceReplacers:
template<class Output>
SpaceReplacer<Output> makeReplacer(Output output_func) {
return SpaceReplacer<Output>{output_func, 0};
}
Reading one string, returning new string
It's now easy to write a function that replaces spaces in a string.
std::string replaceSpaces(std::string const& input) {
std::string output_string;
// We output chars by appending them to the output string
auto output_func = [&](char c) { output_string += c; };
auto replacer = makeReplacer(output_func);
for(char c : input) {
replacer.input(c);
}
return output_string;
}
Reading input from file, replacing spaces and returning a string
Because we wrote a really generic SpaceReplacer class, we can modify the function so that it'll read input directly from a FILE*
std::string replaceSpaces(FILE* file) {
std::string output_string;
auto output_func = [&](char c) { output_string += c; };
auto replacer = makeReplacer(output_func);
while(true) {
int input_char = fgetc(file);
if(input_char == EOF) {
break;
}
replacer.input(input_char);
}
return output_string;
}
Reading input from one file, immediately appending it to different file with spaces replaced
We can also read directly from one file, and output directly to another file, with no delay. This might be useful if you were processing a very large amount of data.
void replaceSpaces(FILE* input_file, FILE* output_file) {
auto output_func = [=](char c) { fputc(c, output_file); };
auto replacer = makeReplacer(output_func);
while(true) {
int input_char = fgetc(input_file);
if(input_char == EOF) {
break;
}
replacer.input(input_char);
}
}
In this case, you need to use another string for the result.
#include <cstring>
#include <iostream>
#include <fstream>
#include <string>
using namespace std;
int main()
{
// Get the String
string result, str = "this essay needs each word to be numbered qwe qwe wqe qwe qwe qwe q";
int num = 0;
int i;
// read string character by character.
for (i=0; i < str.length(); i++) {
// Changing the loaded character
// to a number if it's a space.
if (str[i] == ' ')
result+=std::to_string(++num);
else
result+=str[i];
}
// testing outputs
cout<<result<<endl;
cout<<num;
ofstream file;
file.open ("numbered.txt");
file << result;
file.close();
return 0;
}
You have to replace it with a character, not by a number.
str[i] = num+'0';

How to start reading file from a particular position c++

I am reading a file using fstream and getline functions. I want to give a starting position e.g. my file has 13 lines I want to start reading it from 7th line for example. Here is my code:
#include<iostream>
#include <stdlib.h>
#include <string>
#include <vector>
#include<iterator> // for iterators
#include<map>
using namespace std;
int main()
{
string line;
int start= 7;
unsigned long int index;
For( int z=1; z<=13; z++){
if (f_node.is_open())
{
getline(f_node, line);
if ((line.find("$EndNodes") != string::npos))
{
cout << "$EndNodes found file closed .... " << endl;
f_node.close();
return false;
}
// Point index.
int i = 0;
int j = line.find_first_of(" ", i);
index = strtoul((line.substr(i, j)).c_str(), NULL, 0);//
}
}
I am reading only indexes and I want to start it from 7th index How to do it?
To discard some number of lines, something like:
#include <fstream>
#include <string>
int main() {
std::ifstream infile{"myfile.txt"};
std::string line;
int starting_line = 7;
// Read and discard beginning lines
for (int n = 1; n < starting_line; n += 1) {
if (!std::getline(infile, line)) {
// Error or premature end of file! Handle appropriately.
}
}
while (std::getline(infile, line)) {
// Do something with the lines you care about.
}
return 0;
}
Except with actual error checking and handling and such.
"there is no way to tell code the starting position like seekg and tellg?" No. NL is just like any other character, it does not receive any special treatment.
You simply must scan the stream, counting the new-line character:
std::istream& seek_line(std::istream& is, const int n, std::ios_base::seekdir way = std::ios_base::beg)
{
is.seekg(0, way);
int i = 0;
char c;
while (is.get(c) && i < n)
if (c == '\n')
++i;
is.putback(c);
return is;
}
And this is how you use the above function:
int main()
{
using namespace std;
ifstream is{ "c:\\temp\\test.txt" };
if (!is)
return -1;
if (!seek_line(is, 3))
return -2;
string s;
getline(is, s);
cout << s << endl;
return 0;
}

I am getting a segmentation fault in this code and can't understand why?

I am trying to code a program where it takes a program as an input and prints out all the comments written in that program in a separate line.
#include <cmath>
#include <cstdio>
#include <vector>
#include <iostream>
#include <algorithm>
using namespace std;
int main() {
string str;
while(getline(cin,str)) {
int i;
// cout<<str;
for(i=0;str[i]!='/' && str[i+1] !='/';i++);
//cout<<i;
for(i;str[i]!='\n';i++) {
// cout<<i;
cout<<str[i];
}
cout<<endl;
}
return 0;
}
I am getting a segmentation fault in this code and I can't understand why. This is part of a code of a problem in hackerrank https://www.hackerrank.com/challenges/ide-identifying-comments/copy-from/12957153
As commented in your question your code is wrong. First you are treating std::string object, returned by getline, as character array. Secondly your for loops never end if there is no // or \n found in input string. So obviously it will crash. Below is the modified code.
#include <cmath>
#include <cstdio>
#include <vector>
#include <iostream>
#include <algorithm>
using namespace std;
int main() {
string str;
while(getline(cin,str)) {
int i;
// cout<<str;
size_t len = str.length();
const char *cstr = str.c_str();
for(i=0; (cstr[i]!='/' && cstr[i+1] !='/' && i < len); i++)
//cout<<i;
for(; cstr[i]!='\n' && i < len;i++) {
// cout<<i;
cout<<cstr[i];
}
cout<<endl;
}
return 0;
}
int main() {
while(getline(cin,str)) {
int i, len = str.size();
//always make sure that you are not accessing
//contents after your string has ended
for(i=0; i < (len - 1) && !(str[i] == '/' && str[i+1] == '/'); i++);
//note that i here might be the last alphabet
//if there's no comment
if(i < len && str[i] != '/')
i++;
//checking if str[i] != '\n' is not a good idea
//as c++ stl strings are not temrinated by '\n'
if(i < len) {
for(; i < len; i++)
cout << str[i];
cout << endl;
}
}
return 0;
}
Also note that both of the following codes won't terminate at the 4th character, c++ stl strings are not terminated by these characters.
string str = "hahahaha";
str[4] = '\n';
cout << str;
str[4] = '\0';
cout << str;
This is much easier to write and probably much faster than the other solutions to date.
#include <iostream>
int main()
{
std::string str;
while (std::getline(std::cin, str))
{
size_t loc = str.find("//");
if (loc != str.npos)
{
std::cout << str.substr(loc + 2)<< std::endl;
}
}
return 0;
}
It is also wrong.
Here is a nice, clean, and simple state machine version. Also pretty close to worst-case for speed. Thing is it's closest to being right, even though it is also wrong.
#include <iostream>
enum states
{
seeking1,
seeking2,
comment
};
int main()
{
std::string str;
while (std::getline(std::cin, str))
{
states state = seeking1;
for (char ch:str)
{
switch (state)
{
case seeking1:
if (ch == '/')
{
state = seeking2;
}
break;
case seeking2:
if (ch == '/')
{
state = comment;
}
else
{
state = seeking1;
}
break;
case comment:
std::cout << ch;
break;
}
}
if (state == comment)
{
std::cout << std::endl;
}
}
return 0;
}
Why are these approaches all wrong? Consider the line
cout << "Hi there! I am \\Not A Comment!" << endl;`
You can't just look at the \\, you also need the context. This is why the state machine above is the better option. It can be modified to handle, at the very least, states for handling strings and block comments.