How to separate strings into 2D vector? - c++

This is the file with data that I'm reading from:
MATH201,Discrete Mathematics
CSCI300,Introduction to Algorithms,CSCI200,MATH201
CSCI350,Operating Systems,CSCI300
CSCI101,Introduction to Programming in C++,CSCI100
CSCI100,Introduction to Computer Science
CSCI301,Advanced Programming in C++,CSCI101
CSCI400,Large Software Development,CSCI301,CSCI350
CSCI200,Data Structures,CSCI101
I have successfully read from the file and stored this information in a 2D Vector, but when I check the size of specific rows using course.info[x].size() it appears that its only counting the strings that have spaces between them. So for example, course.info[2].size() returns 2, whereas I would rather it would return 3. Essentially I want it to count each bit of information that is separated by a comma instead of a space. If I use while (getline(courses, line, ',') it puts the information separated by a comma in their own row, which is not what I want.
void LoadFile() {
vector<vector<string>> courseInfo;
ifstream courses;
courses.open("CourseInfo.txt");
if (!courses.is_open()) {
cout << "Error opening file";
}
if (courses) {
string line;
while (getline(courses, line)) {
courseInfo.push_back(vector<string>());
stringstream split(line);
string value;
while (split >> value) {
courseInfo.back().push_back(value);
}
}
}
for (int i = 0; i < courseInfo.size(); i++) {
for (int j = 0; j < courseInfo[i].size(); j++)
std::cout << courseInfo[i][j] << ' ';
std::cout << '\n';
}

getline(.., .., ',') is the tool for the job, but you need to use it in a different place.
Replace while (split >> value) with while (getline(split, value, ',').

Related

Binary search check if string contains a string

I have a problem with using binary_search, it works, but only if the whole string is inserted as the search-key
I want it to work without searching after whole string, but just a key word and return "found" if a string(search-key) is part of another string(from sorted vector of strings)
case 5: // case til søgning efter telefonnummer
cout << "Indtast telefonnummer til soegning: " << endl;
getline(cin >> ws, key);
vector<string> mylines_sorted;
for (int i = 0; i < mylines.size(); i++) {
mylines_sorted.push_back(mylines[i]); // vector of strings is transferred to new vector of strings
}
sort(mylines_sorted.begin(), mylines_sorted.end());
for (int i = 0; i < mylines.size(); i++) {
cout << mylines_sorted[i] << endl; // just a check if data is sorted
}
bool result = binary_search(mylines_sorted.begin(), mylines_sorted.end(), key);
cout << result << endl; // another check
if (result == false) {
cout << "Soegning gav intet...!" << endl;
}
else {
cout << "Soegning: " << key << " findes i datafil!" << endl;
}
break;
}
return 0;
string line;
vector<string> mylines;
while (getline(database, line)) {
mylines.push_back(line);
}
I don't know if this part is relevant, I dont think so, but I transfer data from data file to vector of strings
struct Data {
char navn[80];
char addresse[80];
int alder;
unsigned int tlf;
};
There's a very simple way to get "words" from a string: Put the string into an std::istringstream and use std::istream_iterator<std::string> to get words out of it.
Combine this with the vectors insert function to add the strings to the vector you sort and search.
For example something like this:
// For each line...
for (auto const& line : mylines)
{
// Put the line into an input string stream
std::istringstream iss(line);
// Read from the string stream, adding words to the sorted_mylines vector
sorted_mylines.insert(end(sorted_mylines),
std::istream_iterator<std::string>(iss),
std::istream_iterator<std::string>());
}
After the above, sorted_mylines will contain all the words from all the lines in mylines.
You can now sort it and search for individual words. Or just skip the sorting and do a linear search.
Considering your edit, and the structure you use, I suggest you first read the file, parse each line into the corresponding structure, and create a vector of that instead of working with lines.
Then you could easily search for either name (which I recommend you split into separate first and last name) or address (which I recommend you also split up into its distinct parts, like street name, house number, postal code, etc.).
If you split it up then it will become much easier to search for specific parts. If you want a more generic search then to a linear loop over all entries, and look in all relevant structure members.

How do i read a csv text file into a 2D array?

This is the sample text file content:
5 //columns
Id,Age,history,chemistry,biology //column names
100// number of data rows
3245167,12,45,78,12 //data rows separated by commas
30980424,10,26,38,98
and so on..
This is the general code i have so far:
int main()
{
//prints out input from file.
ifstream myFile;
myFile.open("WRITE FILE NAME");
while(Myfile.good()) { // good means while there is smthg in the file keep reading it
// until you reach to the end.
string line;
getline(myFile, line, ','); //read text until comma, then stop and store in variable.
cout << line << endl;
}
return 0;
}
You have a general outline of parsing the file itself, the data will be read from the file left to right. So there's two things I'd recommend for you to do at this point since you've already parsed the data. You'll probably want something to hold it like a queue to store and hold all the data and a double for loop to place it into the 2D array so like this:
std::queue<string> holder;
std::string myArray[row][col];
getline(myFile, line, ',');
holder.push(line);
for(int i=0; i < row; i++)
{
for(int j=0; j < col; j++)
{
myArray[i][j] = holder.pop();
}
}

Reading only numbers from a file, from a specific line

I am trying to read from a data file that contains a header which is 4 lines, and also has a list of numbers that I will be storing into a 2d int array
for example
header
header
header
header
int
int
int
int
......
I need to somehow skip these header lines which contain text and only use the int lines and store them into the aforementioned 2d array. When I open the file and search through it, it doesn't store any values at all because of the text at the very start. I've tried multiple if statements and other things to get around this, but has worked so far.
int main()
{
ifstream imageFile;
imageFile.open("myfile");
if (!imageFile.is_open())
{
exit (EXIT_FAILURE);
}
int test2[16][16];
int word;
imageFile >> word;
while (imageFile.good())
for (int i = 0; i < 16; i++)
{
for (int j = 0; j < 16; j++)
{
test2[i][j] = word;
imageFile >> word;
}
}
}
As said in the comments, you need to first read the headers - here I just store the headers in a trash variable which is a string that's being overwritten every time I store new header:
std::string trash;
for (int i =0; i < 4; i++)
std::getline(imageFile, trash);
This part goes after you check if file opened correctly and will be directly followed by your original code where you declare the 2D array and read the integers.
As it was also said in the comments you need std::getline that reads every header line as a whole and not a word at a time which was the first version of my answer (imageFile >> trash;).
You can do this just by regex and patterns (Modify code for 2d array this is just example for how you can extract numbers from file or string):
std::string ss;
ifstream myReadFile;
myReadFile.open("foo.txt");
char output[100];
if (myReadFile.is_open()) {
while (!myReadFile.eof()) {
myReadFile >> output;
ss.append(output);
ss.append("\n");
}
}
myReadFile.close();
std::regex rx(R"((?:^|\s)([+-]?[[:digit:]]+(?:\.[[:digit:]]+)?)(?=$|\s))"); // Declare the regex with a raw string literal
std::smatch m;
std::string str = ss;
while (regex_search(str, m, rx)) {
std::cout << "Number found: " << m[1] << std::endl; // Get Captured Group 1 text
str = m.suffix().str(); // Proceed to the next match
}
Output:
Number found: 612
Number found: 551
Number found: 14124

First letters being erased when reading from data file c++

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.

Tokenization: Refer to particular tokens after read multiple .dat files

I've used the code below to read multiple .dat files into 2D vectors and print out tokens values. However, I need to know if all tokens values will be stored in the memory after the compilation completes, and how can I refer to a certain element like token[3][27] as an example for further processing:
for (int i = 0; i < files.size(); ++i) {
cout << "file name: " << files[i] << endl;
fin.open(files[i].c_str());
if (!fin.is_open()) {
cout<<"error"<<endl;
}
std::vector<vector<string>> tokens;
int current_line = 0;
std::string line;
while (std::getline(fin, line))
{
cout<<"line number: "<<current_line<<endl;
// Create an empty vector for this line
tokens.push_back(vector<string>());
//copy line into is
std::istringstream is(line);
std::string token;
int n = 0;
//parsing
while (getline(is, token, DELIMITER))
{
tokens[current_line].push_back(token);
cout<<"token["<<current_line<<"]["<<n<<"] = " << token <<endl;
n++;
}
cout<<"\n";
current_line++;
}
fin.clear();
fin.close();
}
Do I need to create 2D vector for each file? can that be achieved during the runtime in C++ ?
If you want to use your 2D vector further you need to declare it outside the for loop. The way you did it you create a local variable which is destroyed each and every loop iteration.
for (int i = 0; i < files.size(); ++i) {
std::vector<vector<string>> tokens(i);
}
tokens[0][0]; // you can't do it here: variable tokens not declared in this scope
Of course you can use your tokens container right after the while loop, addressing certain token just the way you mentioned it.
To use tokens outside the for loop you can either make a 3D vector holding files,lines,tokens, or make this a function which returns 2D vector for certain file, and then you can process it.