How can I format text "justified" so it aligns to the left and right side for a given width?
int main()
{
printJustified("A long text with many words. "
"A long text with many words. "
"A long text with many words. "
"A long text with many words. "
"A long text with many words.");
}
Expected output:
A long text with many words. A long text with
many words. A long text with many words. A
long text with many words.
A simple example how to solve this problem is this:
#include <iostream>
#include <sstream>
#include <list>
const int pageWidth = 78;
typedef std::list<std::string> WordList;
WordList splitTextIntoWords( const std::string &text )
{
WordList words;
std::istringstream in(text);
std::copy(std::istream_iterator<std::string>(in),
std::istream_iterator<std::string>(),
std::back_inserter(words));
return words;
}
void justifyLine( std::string line )
{
size_t pos = line.find_first_of(' ');
if (pos != std::string::npos) {
while (line.size() < pageWidth) {
pos = line.find_first_not_of(' ', pos);
line.insert(pos, " ");
pos = line.find_first_of(' ', pos+1);
if (pos == std::string::npos) {
pos = line.find_first_of(' ');
}
}
}
std::cout << line << std::endl;
}
void justifyText( const std::string &text )
{
WordList words = splitTextIntoWords(text);
std::string line;
for (const std::string& word : words) {
if (line.size() + word.size() + 1 > pageWidth) { // next word doesn't fit into the line.
justifyLine(line);
line.clear();
line = word;
} else {
if (!line.empty()) {
line.append(" ");
}
line.append(word);
}
}
std::cout << line << std::endl;
}
int main()
{
justifyText("This small code sample will format a paragraph which "
"is passed to the justify text function to fill the "
"selected page with and insert breaks where necessary. "
"It is working like the justify formatting in text "
"processors.");
return 0;
}
It works like this:
First the text is split into words.
The words are added to lines until the line can not take more words.
For each line, space are added between the words until the line matches the requested width.
Related
I am trying to parse a large text file and split it up into single words using strtok. The delimiters remove all special characters, whitespace, and new lines. For some reason when I printf() it, it only prints the first word and a bunch of (null) for the rest.
ifstream textstream(textFile);
string textLine;
while (getline(textstream, textLine))
{
struct_ptr->numOfCharsProcessedFromFile[TESTFILEINDEX] += textLine.length() + 1;
char *line_c = new char[textLine.length() + 1]; // creates a character array the length of the line
strcpy(line_c, textLine.c_str()); // copies the line string into the character array
char *word = strtok(line_c, delimiters); // removes all unwanted characters
while (word != nullptr && wordCount(struct_ptr->dictRootNode, word) > struct_ptr->minNumOfWordsWithAPrefixForPrinting)
{
MyFile << word << ' ' << wordCount(struct_ptr->dictRootNode, word) << '\n'; // writes each word and number of times it appears as a prefix in the tree
word = strtok(NULL, delimiters); // move to next word
printf("%s", word);
}
}
Rather than jumping through the hoops necessary to use strtok, I'd write a little replacement that works directly with strings, without modifying its input, something on this general order:
std::vector<std::string> tokenize(std::string const &input, std::string const &delims = " ") {
std::vector<std::string> ret;
int start = 0;
while ((start = input.find_first_not_of(delims, start)) != std::string::npos) {
auto stop = input.find_first_of(delims, start+1);
ret.push_back(input.substr(start, stop-start));
start = stop;
}
return ret;
}
At least to me, this seems to simplify the rest of the code quite a bit:
std::string textLine;
while (std::getline(textStream, textLine)) {
struct_ptr->numOfCharsProcessedFromFile[TESTFILEINDEX] += textLine.length() + 1;
auto words = tokenize(textLine, delims);
for (auto const &word : words) {
MyFile << word << ' ' << wordCount(struct_ptr->dictRootNode, word) << '\n';
std::cout << word << '\n';
}
}
This also avoids (among other things) the massive memory leak you had, allocating memory every iteration of your loop, but never freeing any of it.
Move printf two lines UP.
while (word != nullptr && wordCount(struct_ptr->dictRootNode, word) > struct_ptr->minNumOfWordsWithAPrefixForPrinting)
{
printf("%s", word);
MyFile << word << ' ' << wordCount(struct_ptr->dictRootNode, word) << '\n'; // writes each word and number of times it appears as a prefix in the tree
word = strtok(NULL, delimiters); // move to next word
}
As #j23 pointed out, your printf is in the wrong location.
As #Jerry-Coffin points out, there are more c++-ish and modern ways to accomplish, what you try to do. Next to avoiding mutation, you can also avoid copying the words out of the text string. (In my code below, we read line by line, but if you know your whole text fits into memory, you could as well read the whole content into a std::string.)
So, using std::string_view avoids to perform extra copies, it being just something like a pointer into your string and a length.
Here, how it looks like, for a use case, where you need not store the words in another data structure - some kind of one-pass processing of words:
#include <iostream>
#include <fstream>
#include <string>
#include <string_view>
#include <cctype>
template <class F>
void with_lines(std::istream& stream, F body) {
for (std::string line; std::getline(stream,line);) {
body(line);
}
}
template <class F>
void with_words(std::istream& stream, F body) {
with_lines(stream,[&body](std::string& line) {
std::string_view line_view{line.cbegin(),line.cend()};
while (!line_view.empty()) {
// skip whitespaces
for (; !line_view.empty() && isspace(line_view[0]);
line_view.remove_prefix(1));
size_t position = 0;
for (; position < line_view.size() &&
!isspace(line_view[position]);
position++);
if (position > 0) {
body(line_view.substr(0,position));
line_view.remove_prefix(position);
}
}
});
}
int main (int argc, const char* argv[]) {
size_t word_count = 0;
std::ifstream stream{"input.txt"};
if(!stream) {
std::cerr
<< "could not open file input.txt" << std::endl;
return -1;
}
with_words(stream, [&word_count] (std::string_view word) {
std::cout << word_count << " " << word << std::endl;
word_count++;
});
std::cout
<< "input.txt contains "
<< word_count << " words."
<< std::endl;
return 0;
}
Hello friends at stackoverflow!
I have written a program that saves 3 strings to a textfile. The code to write is:
void my_class::save_file(const char* text) {
for(auto ad: ad_list) {
std::ofstream outputFile;
outputFile.open(text, std::ios::app);
if (outputFile.is_open()) {
outputFile << ad.string1 << "|" << ad.string2 << "|" << ad.string3 << "|" << std::endl;
outputFile.close();
}
}
}
This gives me the file with the content:
string1|string2|string3|
<- //extra line here
When i read from this file I do it with the following function:
void my_class::read_file(const char* text) {
// read file to stringsteam
std::stringstream ss;
std::ifstream fp (text);
if (fp.is_open()) {
ss << fp.rdbuf();
}
fp.close();
string file_contents = ss.str();
// split to lines
auto lines = splitString(file_contents, "\n");
// split to parts
for (auto ad_text: lines) { // <-- this is the possibly the issue
auto ad_parts = splitString(ad_text, "|");
string string1 = ad_parts[0];
string string2 = ad_parts[1];
string string3 = ad_parts[2];
auto new_class = MyClass(string1, string2, string3);
//Adds (push_back) the class to its vector
add_class(new_class);
}
}
vector<string> my_class::splitString(string text, string delimiter) {
vector<string> parts;
size_t start = 0;
size_t end = 0;
while((end = text.find(delimiter, start)) != std::string::npos) {
size_t length = end - start;
parts.push_back(text.substr(start, length));
start = end + delimiter.length();
}
start = end + delimiter.length();
size_t length = text.length() - start;
parts.push_back(text.substr(start, length));
return parts;
}
Result:
string1|string2|string3|
string1|string2|string3|
I'm pretty sure this copy is a result of the new line left behind by the write function. The read function splits the text into lines, and that would be 2 lines.
I am currently splitting the lines in this loop "for (auto ad_text: lines)", and what I want to do is basically ( lines - 1 ). I do not understand how I can do that with the way I am interating through the for loop.
Thanks in advance!
You can simplify your splitString function to look like below. Note the second parameter to splitString is a char now and not a std::string.
//note the second parameter is a char and not a string now
vector<string> splitString(string text, char delimiter) {
vector<string> parts;
std::string words;
std::istringstream ss(text);
while(std::getline(ss, words,delimiter ))
{
parts.push_back(words);
}
return parts;
}
For the above modification to work you have to make 2 additional changes:
Change 1: Replace auto lines = splitString(file_contents, "\n"); to:
auto lines = splitString(file_contents, '\n');
Change 2: Replace auto ad_parts = splitString(ad_text, "|"); to:
auto ad_parts = splitString(ad_text, '|');
I'm having difficulty creating a function that reverse the order of the sentence around. I've read many functions on how to recursively reverse the letters around and I have successfully done so, but I do not want to reverse the letters in the words. I want to reverse the placement of the words in the sentence.
Example would be:
This is a sentence.
sentence. a is This
This is my code so far. How do I go from reversing order of letters of the entire sentence to placement order of words in a sentence?
The output of the current code would provide: !dlroW olleH
void reverse(const std::string str)
{
int length = str.size();
if(length > 0)
{
reverse(str.substr(0,length-1));
std::cout << str[0];
}
}
Edit: Additional question. If this was a char array would the logic be different?
Simplify your logic by using a std::istringstream and a helper function. The program below works for me.
#include <sstream>
#include <iostream>
void reverse(std::istringstream& stream)
{
std::string word;
if ( stream >> word )
{
reverse(stream);
std::cout << word << " ";
}
}
void reverse(const std::string str)
{
std::istringstream stream(str);
reverse(stream);
std::cout << std::endl;
}
int main(int argc, char** argv)
{
reverse(argv[1]);
return 0;
}
// Pass string which comes after space
// reverse("This is a sentence.")
// reverse("is a sentence.")
// reverse("a sentence.")
// reverse("sentence.")
// will not find space
// start print only word in that function
void reverse(const std::string str)
{
int pos = str.find_first_of(" ");
if (pos == string::npos) // exit condition
{
string str1 = str.substr(0, pos);
cout << str1.c_str() << " " ;
return;
}
reverse(str.substr(pos+1));
cout << str.substr(0, pos).c_str() << " ";
}
Simple to understand:
void reverse(const std::string str)
{
int pos = str.find_first_of(" ");
if (pos != string::npos) // exit condition
{
reverse(str.substr(pos + 1));
}
cout << str.substr(0, pos).c_str() << " ";
}
std::vector<std::string> splitString(const std::string &s, char delim) {
std::stringstream ss(s);
std::string item;
std::vector<std::string> tokens;
while (getline(ss, item, delim)) {
tokens.push_back(item);
}
return tokens;
}
void reverseString(const std::string& string) {
std::vector<std::string> words = splitString(string, ' ');
auto end = words.rend();
for (auto it = words.rbegin(); it <= end; it++) {
std::cout << *it << std::endl;
}
}
reverseString("This is a sentence.");
You can split input and print them in inverse order
Or if you want to use recursive structure just move the cout after calling a function like this:
void reverse(const std::string str)
{
std::stringstream ss(str);
std::string firstWord, rest;
if(ss >> firstWord)
{
getline(ss , rest);
reverse(rest);
std::cout << firstWord << " ";
}
}
I am not a C++ programmer, but I would create another array (tempWord[ ]) to store individual word.
Scan each word and store them into tempWord array. In your case, the words are separated by space, so:
a.get the index of the next space,
b substring to the index of the next space and
c. you should get {"This", "is", "a", "sentence."}
Add them up again reversely:
a. loop index i from "tempWord.length -1" to "0"
b. new String = tempWord[i]+" ";
print out result.
I want to read data from stream, which has specific format, such as:
"number:name_that_can_contain_spaces:string,string,string..." without quotes where ... means that I dont know how many strings are there separated with commas and strings can have spaces before and after it but not in the middle of string, I want to stop reading at new line
I only come up with using getline() and store each line into string, but I dont know how to continue, if there is something like strtok(line, ":",":",",","\n") which would parse it for me or I have to parse it myself character by character
example of valid line format is:
54485965:abc abc abc: some string, next string , third string\n
parsed result would be:
int 54485965
string "abc abc abc"
string "some string"
string "next string"
string "third string"
You can read line with std::getline and then split it with std::string::find and std::string::substr. In the code below we read line from file data, then find : (so everything before it becomes number which we parse into int with std::stoi) and throw away first part. Similar we do it with name. And in the end we fill std::list with strings separated by ,.
#include <iostream>
#include <fstream>
#include <string>
#include <list>
#include <exception>
#include <stdexcept>
struct entry {
std::string name;
int number;
std::list<std::string> others;
};
int main(int argc, char** argv) {
std::ifstream input("data");
std::list<entry> list;
std::string line;
while(std::getline(input, line)) {
entry e;
std::string::size_type i = 0;
/* get number from line */
i = line.find(":");
if(i != std::string::npos) {
e.number = stoi(line.substr(0, i));
line = line.substr(i + 1);
} else {
throw std::runtime_error("error reading file");
}
/* get name from line */
i = line.find(":");
if(i != std::string::npos) {
e.name = line.substr(0, i);
line = line.substr(i + 1);
} else {
throw std::runtime_error("error reading file");
}
/* get other strings */
do {
i = line.find(",");
e.others.push_back(line.substr(0, i));
line = line.substr(i + 1);
} while(i != std::string::npos);
list.push_back(e);
}
/* output data */
for(entry& e : list) {
std::cout << "name: " << e.name << std::endl;
std::cout << "number: " << e.number << std::endl;
std::cout << "others: ";
for(std::string& s : e.others) {
std::cout << s << ",";
}
std::cout << std::endl;
}
return 0;
}
In a C++ code, I'm trying to search for a word in a sentence but it keeps doing partial search. I want it to search only for the complete word not parts of it too, any help?
size_t kk;
string word="spo";
string sentence="seven spoons";
kk=sentence.find(word);
if (kk !=string::npos)
cout << "something" << endl;
It sounds like what you want is handled by the concept of word boundaries or word characters in regular expressions.
Here's a program that will return only a complete match. That is, it will only return a word if that word completely matches the exact word you're searching for. If some word in sentence has your target word as a strict substring then it will not be returned.
#include <regex>
#include <string>
#include <iostream>
int main() {
std::string word = "spo"; // spo is a word?
std::string sentence = "seven spoons";
std::regex r("\\b" + word + "\\b"); // the pattern \b matches a word boundary
std::smatch m;
if (std::regex_search(sentence, m, r)) { // this won't find anything because 'spoons' is not the word you're searching for
std::cout << "match 1: " << m.str() << '\n';
}
sentence = "what does the word 'spo' mean?";
if (std::regex_search(sentence, m, r)) { // this does find the word 'spo'
std::cout << "match 2: " << m.str() << '\n';
}
}
Or alternatively maybe you mean you want to find any word that matches a partial word you're searching for. regex can do that as well:
std::string partial_word = "spo";
std::regex r("\\w*" + partial_word + "\\w*"); // the pattern \w matches a word character
This produces:
match 1: spoons
match 2: spo
There are a bunch of options here:
a) Search for [space]WORD[space] instead of just WORD
string word="spo";
string sentence="seven spoons";
kk=sentence.find(" "+word+" ");
Note that this wont work, if your words are separated by newline characters or other white spaces.
b) Split the string into words, store them in a vector, and check if the desired word is somewhere in the vector, by using std::find.
stringstream parser(sentence);
istream_iterator<string> start(parser);
istream_iterator<string> end;
vector<string> words(start, end);
if(find(words.begin(), words.end(), word)!=words.end()) cout<<"found!";
If you're gonna search for words often, this maybe the best choice, since you can store the vector somewhere for future reference, so you don't have to split it. Also - if you want this to work, be sure to #include <algorithm> and #include <vector>.
c) Search for the word and check if isspace(string[position-1]) && isspace(string[position+wordLength])
string word="spo";
string sentence="seven spoons";
kk=sentence.find(" "+word+" ");
if(kk!=string::npos){
if((kk==0 || isspace(sentence[kk-1])) && (kk+word.length()==sentence.length() || isspace(kk+word.length()+1)))
cout << "found!";
}
Something like this :
std::size_t kk;
std::string word="spoo";
std::string sentence="seven spoons tables";
std::stringstream ss(sentence) ;
std::istream_iterator<std::string> f ;
auto it =std::find_if( std::istream_iterator<std::string> (ss),
f,
[=](const std::string& str){
return str == word;
}
);
if(it != f )
std::cout << "Success" <<std::endl;
See here
I think the best way is to split your string using whitespace and punctuation characters as delimiters, then use std::find on the result.
#include <boost/algorithm/string.hpp>
#include <vector>
#include <string>
#include <algorithm>
int main()
{
std::string word="spo";
std::string sentence="seven spoons";
std::vector<std::string> words;
boost::split(words, sentence, boost::is_any_of("\n\t .,!?\"()"));
auto match = std::find(begin(words), end(words), word);
if (match != end(words))
{
// Found it!
}
else
{
// Not there.
}
}
string word="spo";
string sentence="seven spoons";
string::size_type nIndex = sentence.find( word, 0 );
if( nIndex != string::npos )
{
if ((nIndex + word.length() + 1) == sentence.length())
{
cout << "Found" << endl;
}
else
{
string::size_type nSpace = sentence.find( " ", nIndex );
if (nSpace == (nIndex + word.length()))
{
cout << "Found" << endl;
}
}
}
else
{
cout << "No Match" << endl;
}
This seems to have worked.
#include <string>
/*find word in sentence and return the index of first occurrence*/
int find_whole(string sentence,string word){
size_t pos=sentence.find(word);
size_t offset=pos+sentence.size()+1;
if((pos!=string::npos) && (sentence.substr(pos,offset)==word))
return pos;
return string::npos;
}