i am trying to get my file to remove the leading and trailing space but it does not work.
this is the txt file contents:
392402 wench
I have tried printing out my code, and this is what is displayed.
first: 392402 wench second:
I want it to display this instead
first: 392402 second: wench
this is my code
void readFile(const string &fileName) {
int limit;
ifstream ifs(fileName);
string::size_type position;
key_type item;
mapped_type count;
string line;
if (ifs.is_open()) {
ifs >> limit;
for (int i = 0; i < limit; i++) {
getline(ifs, line);
position = line.find(" ", 0);
auto c = line.substr(position + 1);
item = line.substr(0, position);
cout << "first: " << c << " second: " << item << endl;
value_type value(item, count);
values.push_back(value);
}
} else {
cout << "Can't open file.";
}
what am i doing wrong? Thank you
The two biggest mistakes you're making are (a) not checking your values for expected output as you go, and (b) not running your code in a debugger to see what is really happening. If you had, the values of position, c, and item would have been blatantly wrong, and you could then surmise where to go from there.
Belaying the highly-likely possibility that the loop iteration is broken from inception because you never consumed the remainder of the entry line containing input, let's look at the actual data and what you're asking of it with your code.
We read this entire line:
392402 wench
You then ask "find the first single-space string in this line" via this code:
position = line.find(" ", 0);
Well, that would be here:
392402 wench
^here
So position is zero (0). You then ask for the sub-string, starting a that position + 1, through the end of the string with this code:
auto c = line.substr(position + 1);
Therefore c now contains (leading space removed via the +1):
392402 wench
Now we build item, which is done with this line:
item = line.substr(0, position);
Remember, position is zero, so you're asking for the string, starting at location 0, length 0. As you can imagine, that isn't going to amount to anything. So now item is an empty string.
Finally, the output statement:
cout << "first: " << c << " second: " << item << endl;
will produce:
first: 392402 wench second:
I.e. exactly what you're seeing. And that's it. Clearly this is wrong.
Alternative
Use better error checking, value checking, and a string stream for per-line extraction. The following code doesn't give two cents about your type aliases (mainly because you didn't include them anyway and I'd rather not loft any guesses as to their origin).
#include <iostream>
#include <fstream>
#include <sstream>
#include <string>
#include <limits>
// Expects a file with the following format:
// count
// name1 value1
// name2 value2
// ...
void readFile(const std::string &fileName)
{
std::ifstream ifs(fileName);
if (ifs.is_open())
{
int limit;
if (ifs >> limit && limit > 0)
{
// consume through end of line.
ifs.ignore(std::numeric_limits<std::streamsize>::max(), '\n');
// repeat until `limit` iterations or stream error/eof
std::string line;
for (int i = 0; i < limit && std::getline(ifs, line); i++)
{
std::istringstream iss(line);
// extract line values. Note these *can* be formatted
// extraction for things besides just strings
std::string first, second;
if (iss >> first >> second)
{
std::cout << "first: " << first << " second: " << second << '\n';
// TODO: whatever you want to do with first/second
}
}
}
ifs.close();
}
else
{
std::cerr << "Can't open file: " << fileName << '\n';
}
}
Note: The above code will NOT work for remaining-line-content as the expected second value. E.g. It will not process something like this as you may first expect:
10000 this is a multi-word description
will produce this:
first: 10000 second: this
which is considerably different than what you may be expecting:
first: 10000 second: this is a multi-word description
There was no suggestion in the original post such support was mandatory, though adding it wouldn't be terribly difficult to add. If it is a requirement, I leave that task to you.
Related
I'm doing the assignment and I'm at the end of my powers. Right now I can't figure out what's missing or what I could change.
I need the program to read me a file. If it finds the beginning of the search word, it lists the word and its meaning. If he finds it more than once, he writes only that word without meaning.
Right now, if the program finds more words, it writes the meaning for the first word and writes the word for the other words found.
I don't know what other cycle I could use. If you could help me, I would be grateful.
#include <iostream>
#include <fstream>
#include <sstream>
#include <string>
#include<bits/stdc++.h>
using namespace std;
int main()
{
ifstream dictionary("dictionary.txt");
if(!dictionary.is_open()){
cout<< "File failed to open" << endl;
return 0;
}
int option;
cout << "1.<starting>" << endl;
cout << "4.<stop>" << endl;
cin >> option;
string find_word;
string word, meaning;
string line;
string found;
int count = 0;
if (option == 1)
{
cout << "Find the meaning of the word beginning with the characters:";
cin >> find_word;
while (getline(dictionary,line))
{
stringstream ss(line);
getline (ss, word, ';');
getline (ss, meaning, ';');
if (word.rfind(find_word, 0) != string::npos)
{
count++;
if (count <=1)
{
found = word + meaning;
cout << found << endl;
}
if (count >= 2)
{
found = word ;
cout << found << endl;
}
}
}
}
if (option == 4)
{
return 0;
}
dictionary.close();
return 0;
}
EDIT
dictionary.txt looks like this:
attention; attentionmeaning
attention; attentionmeaning2
computer; computermeaning
criminal; criminalmeaning
boat; boatmeaning
alien; alienmeaning
atter; meaning
.
.
etc.
For example input is:
Find the meaning of the word beginning with the characters: att
this is what i get now (output):
attention attentionmeaning
attention
atter
this is what i expect (desire output):
attention
attention
atter
if program find only one searching word it should write this:
Find the meaning of the word beginning with the characters: bo
output:
boat boatmeaning
As it was already suggested, while reading the file, you don't know if there will be more than one entries matching your search term. That being said, you need some intermediate structure to store all the matching entries.
After you have gathered all the results, you can easily check if the data contains more than one result, in which case you only print the "word" without the meaning. In case there is only one result, you can print the "word" together with its meaning.
The code for that could look something like this:
struct Entry {
std::string name;
std::string meaning;
bool startsWith(const std::string& str) {
return name.find(str) != std::string::npos;
}
};
Entry createEntry(const std::string& line) {
Entry entry;
std::stringstream ss(line);
std::getline(ss, entry.name, ';');
std::getline(ss, entry.meaning, ';');
return entry;
}
int main() {
std::string query = "att";
std::ifstream dictionary("dictionary.txt");
std::vector<Entry> entries;
std::string line;
while (std::getline(dictionary, line)) {
Entry entry = createEntry(line);
if (entry.startsWith(query)) {
entries.emplace_back(std::move(entry));
}
}
for (const Entry& entry : entries) {
std::cout << entry.name << (entries.size() > 1 ? "\n" : " " + entry.meaning + '\n');
}
}
This code could definitely be more optimized, but for the sake of simplicity, this should suffice.
Demo
The problem is that at the first time through the loop you do not know if there is one or more valid words that follow from your string. I would suggest you create an empty list outside the loop, and push all the word and meaning pairs that match onto the list. Then after if the size of the list is 1 you can output the word and meaning pair else use a for loop to loop through and just print the words.
i have a csv file and i have to read this file with only fstream library. There are 8 columns but i will use only first three columns. This file contains 591.000 lines data.
I tried to read like this;
while (retailFile.good()) {
if (i == 0) continue;
getline(retailFile, invoiceNo, ';');
getline(retailFile, stockCode, ';');
getline(retailFile, desc, ';');
getline(retailFile, dummy, ';');
getline(retailFile, dummy, ';');
getline(retailFile, dummy, ';');
getline(retailFile, dummy, ';');
getline(retailFile, dummy);
i++;
}
Tried like that - I wasn't too hopeful - it was a complete disappointment.
How can read very fast? It's ridiculous to keep it in an empty variable. Can't we pass that columns?
To find the end of the line, you have to read through all of the columns in the line looking for the end of the line. This is unavoidable. You do not have to process those unwanted fields though.
Taking inspiration from option two of this linked answer I get something like
//discard first line without looking at it.
if (retailFile.ignore(std::numeric_limits<std::streamsize>::max(), '\n')
{ // ALWAYS test IO transactions to make sure they worked, even something as
// trivial as ignoring the input.
std::string line;
while (std::getline(retailFile, line))
{ // read the whole line
// wrap the line in a stream for easy parsing
std::istringstream stream (line);
if (std::getline(retailFile, invoiceNo, ';') &&
std::getline(retailFile, stockCode, ';') &&
std::getline(retailFile, desc, ';'))
{ // successfully read all three required columns
// Do not use anything you read until after you know it is good. Not
// checking leads to bugs and malware.
// strongly consider doing something with the variables here. The next loop
// iteration will write over them
i++;
}
else
{
// failed to find all three columns. You should look into why and
// handle accordingly.
}
}
}
else
{
// failed to ignore the line. You should look into why and handle accordingly.
}
You probably won't find much of an actual speed difference. Reading files off a disk is usually more time consuming than doing anything with the file unless you do a lot of stuff with the file's data after reading it. There are potentially faster ways to do the splitting of the line, but again the difference is probably buried in the cost of reading the file in the first place.
The question is: What is fast?
In the below demo, I create I file with 591.000 lines. Size is 74MB.
Then I set a bigger input buffer for the std::ifstream, read all lines, parse them, and copy the first 3 entries into the resulting vector. The rest I do ignore.
To avoid that the result is optimized away, I show 50 lines of output.
VS2019, C++17, Release Mode, all optimizations on.
Result: ~2.7s for reading and parsing all lines on my machine. (I must admit that I have 4 SSDs in RAID 0 via PCIe)
#include <iostream>
#include <fstream>
#include <vector>
#include <string>
#include <regex>
#include <array>
#include <chrono>
#include <iterator>
int main() {
// Put whatever filename you want
static const std::string fileName{ "r:\\big.txt" };
// Start Time measurement
auto start = std::chrono::system_clock::now();
#if 0
// Write file with 591000 lines
if (std::ofstream ofs(fileName); ofs) {
for (size_t i = 0U; i < 591000U; ++i) {
ofs << "invoiceNo_" << i << ";"
<< "stockCode_" << i << ";"
<< "description_" << i << ";"
<< "Field_4_" << i << ";"
<< "Field_5_" << i << ";"
<< "Field_6_" << i << ";"
<< "Field_7_" << i << ";"
<< "Field_8_" << i << "\n";
}
}
#endif
auto end = std::chrono::system_clock::now();
auto elapsed = std::chrono::duration_cast<std::chrono::milliseconds>(end - start);
// How long did it take?
std::cout << "Time for writing the file: " << elapsed.count() << " ms\n";
// We are just interested in 3 fields
constexpr size_t NumberOfNeededFields = 3U;
// We expect 591000 lines, give a little bit more
constexpr size_t NumberOfExpectedFilesInFile = 600000U;
// We will create a bigger input buffer for our stream
constexpr size_t ifStreamBufferSize = 100000U;
static char buffer[ifStreamBufferSize];
// The delimtzer for our csv
static const std::regex delimiter{ ";" };
// Main working variables
using Fields3 = std::array<std::string, NumberOfNeededFields>;
static Fields3 fields3;
static std::vector<Fields3> fields{};
// Reserve space to avoid reallocation
fields.reserve(NumberOfExpectedFilesInFile);
// Start timer
start = std::chrono::system_clock::now();
// Open file and check, if it is open
if (std::ifstream ifs(fileName); ifs) {
// Set bigger file buffer
ifs.rdbuf()->pubsetbuf(buffer, ifStreamBufferSize);
// Read all lines
for (std::string line{}; std::getline(ifs, line); ) {
// Parse string
std::copy_n(std::sregex_token_iterator(line.begin(), line.end(), delimiter, -1), NumberOfNeededFields, fields3.begin());
// Store resulting 3 fields
fields.push_back(std::move(fields3));
}
}
end = std::chrono::system_clock::now();
elapsed = std::chrono::duration_cast<std::chrono::milliseconds>(end - start);
std::cout << "Time for parsing the file: " << elapsed.count() << " ms\n";
// Show some result
for (size_t i = 0; i < fields.size(); i += (fields.size()/50)) {
std::copy_n(fields[i].begin(), NumberOfNeededFields, std::ostream_iterator<std::string>(std::cout, " "));
std::cout << "\n";
}
return 0;
}
Can someone please explain why only the first letters are being deleted when reading in from a data file but only on the 1/2/3 parts of the array and not the 0 part? (sorry really don't know how to explain it)(I'll only include part of what I am getting as well as data file)
What i get
GoogleyleSmith01#gmail.comyleman27ecurity question:White rabbit with a watch
Deviantartragonmaster27andalfthegreyNULL
What it's supposed to be
GoogleKyleSmith01#gmail.comKyleman27securityquestion:Whiterabbitwithawatch
DeviantartDragonmaster27GandalfthegreyNULL
And the original data file
Google;KyleSmith01#gmail.com;Kyleman27;security question:White rabbit with a watch;
Deviantart;Dragonmaster27;Gandalfthegrey; NULL;
I won't include all of the code as it shouldn't be relevant to this issue
#include<iostream>
#include <fstream>
#include <string>
#include <vector>
#include<sstream>
using namespace std;
const int NCOLS = 4;
const int NROWS = 10;
void description_and_options(string data[][NCOLS], int count[NCOLS]);
void available_options();
void view_line_data(int choice,string data[][NCOLS]);
int main()
{
ifstream file_name;//create the new file
string user_input_file;//the files name inputed by the user
int stringlength;
string read_in_array[NROWS][NCOLS];
string line;
int counter[10] = { 1,2,3,4,5,6,7,8,9,10 };
string user_option_choice;
string small_exit = "x";
string large_exit = "X";
int view_choice;
cout << "Enter the name of the input file: ";
cin >> user_input_file;
if (user_input_file.length() > 4)// check to see if its more than 4 in length
{
stringlength = user_input_file.length(); //saves length
if (user_input_file.substr(stringlength - 4, 4) == ".txt")//checks to see if its .dat
{
file_name.open(user_input_file.c_str());
if (file_name.fail())
{
cerr << "The file " << user_input_file << " failed to open.\n";//tells user if it fails
exit(1);
}
}
}
else
{
user_input_file += ".txt";//adds .dat to non .dat
file_name.open(user_input_file.c_str());
}
if (file_name.fail())
{
cout << "File failed to open" << endl;
system("PAUSE");
exit(1);
}
for (int row = 0; row <= 9; row++)
{
for (int col = 0; col < 4; col++)
{
if (getline(file_name, line, ';'))
{
file_name.ignore(1, '\n');
read_in_array[row][col] = line;
cout << read_in_array[row][col];
}
}
cout << endl;
}
//[updown][leftright]
file_name.close();
is there anyway to fix this without completely changing the code?
It is ignoring the first character because you tell it to
file_name.ignore(1, '\n');
Is going to ignore the first character in the stream after each call to getline. It looks like you are doing this because you think the ; in the file it still there. What you need to remember about getline is that it discards the delimiter you use. That means it will read until it finds a ; and then it tosses that ; out. This means you do not need to ignore it since it is no longer there.
Just removing the call to ignore is not enough to fix the issue though. Since you are trying to parse an entire line what we need to do is read the line into a stringstream and then call getline on the stream to get the individual parts. This is because just reading to ; is going to capture the newline.
A quick refactor of your code gives you something that should look like
for (int row = 0; row <= 9; row++)
{
std::string temp;
std::getline(file_name, temp)
std::stringstream ss(temp)
for (int col = 0; col < 4; col++)
{
if (getline(ss, line, ';'))
{
read_in_array[row][col] = line;
cout << read_in_array[row][col];
}
}
cout << endl;
}
You are using wrongly ifstream::ignore().
Extracts characters from the input sequence and discards them, until
either n characters have been extracted, or one compares equal to
delim.
file_name.ignore(1, '\n'); always dismiss the first letter. In your case, the first letter after ";" in line.
file_name.ignore(1, '\n'); will make the stream ignore one character from the input.
From reading your code:
For what you call "the 0 part", ignore is not called yet before the first getline in the loop.
For "parts 1/2/3", the ignore statement makes the stream skip the next character
For the remaining parts, there is either a space or a '\n' that was skipped so that the readable letter was not skipped.
I wrote the code below that successfully gets a random line from a file; however, I need to be able to modify one of the lines, so I need to be able to get the line character by character.
How can I change my code to do this?
Use std::istream::get instead of std::getline. Just read your string character by character until you reach \n, EOF or other errors. I also recommend you read the full std::istream reference.
Good luck with your homework!
UPDATE:
OK, I don't think an example will hurt. Here is how I'd do it if I were you:
#include <string>
#include <iostream>
#include <fstream>
#include <cstdlib>
using namespace std;
static std::string
answer (const string & question)
{
std::string answer;
const string filename = "answerfile.txt";
ifstream file (filename.c_str ());
if (!file)
{
cerr << "Can't open '" << filename << "' file.\n";
exit (1);
}
for (int i = 0, r = rand () % 5; i <= r; ++i)
{
answer.clear ();
char c;
while (file.get (c).good () && c != '\n')
{
if (c == 'i') c = 'I'; // Replace character? :)
answer.append (1, c);
}
}
return answer;
}
int
main ()
{
srand (time (NULL));
string question;
cout << "Please enter a question: " << flush;
cin >> question;
cout << answer (question) << endl;
}
... the only thing is that I have no idea why do you need to read string char by char in order to modify it. You can modify std::string object, which is even easier. Let's say you want to replace "I think" with "what if"? You might be better off reading more about
std::string and using find, erase, replace etc.
UPDATE 2:
What happens with your latest code is simply this - you open a file, then you get its content character by character until you reach newline (\n). So in either case you will end up reading the first line and then your do-while loop will terminate. If you look into my example, I did while loop that reads line until \n inside a for loop. So that is basically what you should do - repeat your do-while loop for as many times as many lines you want/can get from that file. For example, something like this will read you two lines:
for (int i = 1; i <= 2; ++i)
{
do
{
answerfile.get (answer);
cout << answer << " (from line " << i << ")\n";
}
while (answer != '\n');
}
I would like to read a text file and input its contents into an array. Then I would like to show the contents of the array in the command line.
My idea is to open the file using:
inFile.open("pigData.txt")
And then to get the contents of the file using:
inFile >> myarray [size]
And then show the contents using a for loop.
My problem is that the file I am trying to read contain words and I don't know how to get a whole word as an element in the array. Also, let's say that the words are divided by spaces, thus:
hello goodbye
Could be found on the file. I would like to read the whole line "hello goodbye" into an element of a parallel array. How can I do that?
Should be pretty straightforward.
std::vector<std::string> file_contents;
std::string line;
while ( std::getline(inFile,line) )
file_contents.push_back(line);
std::vector<std::string>::iterator it = file_contents.begin();
for(; it!=file_contents.end() ; ++it)
std::cout << *it << "\n";
Edit:
Your comment about having "hello goodbye" as element zero and element one is slightly confusing to me. The above code snip will read each line of the file and store that as an individual entry in the array 'file_contents'. If you want to read it and split it on spaces that is slightly different.
For context, you could have provided a link to your previous question, about storing two lists of words in different languages. There I provided an example of reading the contents of a text file into an array:
const int MaxWords = 100;
std::string piglatin[MaxWords];
int numWords = 0;
std::ifstream input("piglatin.txt");
std::string line;
while (std::getline(input, line) && numWords < MaxWords) {
piglatin[numWords] = line;
++numWords;
}
if (numWords == MaxWords) {
std::cerr << "Too many words" << std::endl;
}
You can't have one parallel array. For something to be parallel, there must be at least two. For parallel arrays of words, you could use a declarations like this:
std::string piglatin[MaxWords];
std::string english[MaxWords];
Then you have two options for filling the arrays from the file:
Read an entire line, and the split the line into two words based on where the first space is:
while (std::getline(input, line) && numWords < MaxWords) {
std::string::size_type space = line.find(' ');
if (space == std::string::npos)
std::cerr << "Only one word" << std::endl;
piglatin[numWords] = line.substr(0, space);
english[numWords] = line.substr(space + 1);
++numWords;
}
Read one word at a time, and assume that each line has exactly two words on it. The >> operator will read a word at a time automatically. (If each line doesn't have exactly two words, then you'll have problems. Try it out to see how things go wrong. Really. Getting experience with a bug when you know what the cause is will help you in the future when you don't know what the cause is.)
while (input && numWords < MaxWords) {
input >> piglatin[numWords];
input >> english[numWords];
++numWords;
}
Now, if you really one one array with two elements, then you need to define another data structure because an array can only have one "thing" in each element. Define something that can hold two strings at once:
struct word_pair {
std::string piglatin;
std::string english;
};
Then you'll have just one array:
word_pair words[MaxWords];
You can fill it like this:
while (std::getline(input, line) && numWords < MaxWords) {
std::string::size_type space = line.find(' ');
if (space == std::string::npos)
std::cerr << "Only one word" << std::endl;
words[numWords].piglatin = line.substr(0, space);
words[numWords].english = line.substr(space + 1);
++numWords;
}
Notice how the code indexes into the words array to find the next word_pair object, and then it uses the . operator to get to the piglatin or english field as necessary.
#include <iostream>
#include <fstream>
#include <vector>
#include <string>
using namespace std;
int main()
{
// This will store each word (separated by a space)
vector<string> words;
// Temporary variable
string buff;
// Reads the data
fstream inFile("words.txt");
while(!inFile.eof())
{
inFile>>buff;
words.push_back(buff);
}
inFile.close();
// Display
for(size_t i=0;i<words.size();++i) cout<<words[i]<<" ";
return 0;
}
#include <iostream>
#include <fstream>
#include <string>
#include <vector>
using namespace std;
int main ()
{
vector<string> fileLines;
string line;
ifstream inFile("pigData.txt");
if ( inFile.is_open() ) {
while ( !inFile.eof() ) {
getline(inFile, line);
fileLines.push_back(line);
}
inFile.close();
} else {
cerr << "Error opening file" << endl;
exit(1);
}
for (int i=0; i<fileLines.size(); ++i) {
cout << fileLines[i] << "\n";
}
cout << endl;
return 0;
}