Read file with separator a space and a semicolon - c++

I wrote this to parse a file with numbers, where the separator was just a space. My goal is to read every number of the file and store it in the corresponding index of the matrix A. So, the first number read, should go to A[0][0], second number to A[0][1] and so on.
#include <iostream>
#include <string>
#include <fstream>
using namespace std;
int main() {
const int N = 5, M = 5;
double A[N*M];
string fname("test_problem.txt");
ifstream file(fname.c_str());
for (int r = 0; r < N; ++r) {
for (int c = 0; c < M; ++c) {
file >> *(A + N*c + r);
}
}
for (int r = 0; r < N; ++r) {
for (int c = 0; c < M; ++c) {
cout << *(A + N*c + r) << " ";
}
cout << "\n";
}
cout << endl;
return 0;
}
Now, I am trying to parse a file like this:
1 ;2 ;3 ;4 ;5
10 ;20 ;30 ;40 ;50
0.1 ;0.2 ;0.3 ;0.4 ;0.5
11 ;21 ;31 ;41 ;5
1 ;2 ;3 ;4 ;534
but it will print (thus read) garbage. What should I do?
EDIT
Here is my attempt in C, which also fails:
FILE* fp = fopen("test_problem.txt", "r");
double v = -1.0;
while (fscanf(fp, "%f ;", &v) == 1) {
std::cout << v << std::endl;
}
-1 will always be printed.

The problem with your C example:
warning: format ‘%f’ expects argument of type ‘float*’, but
argument 3 has type ‘double*’ [-Wformat=]
Always and everywhere, turn on warnings (-Wall -Wextra) and do more error checking.
Anyway, to fscanf into a double you need %lf instead of %f.

Given your input format...
1 ;2 ;3 ;4 ;5
...your code...
for (int c = 0; c < M; ++c) {
file >> *(A + N*c + r);
}
...will "eat" the first numeric value, then choke on the first ; separator. The simplest correction would be...
char expected_semicolon;
for (int c = 0; c < M; ++c) {
if (c) {
file >> expected_semicolon;
assert(expected_semicolon == ';'); // if care + #include <cassert>
}
file >> *(A + N*c + r);
}
For whatever it's worth, to add better error checking I'd suggest...
if (std::ifstream file(fname))
{
...use file stream...
}
else
{
std::cerr << "oops\n";
throw or exit(1);
}
...as a general practice for opening file stream.
For looping getting data, using a support macro to give an assertion-like style works well with streams:
#define CHECK(CONDITION, MESSAGE) \
do { \
if (!(CONDITION)) { \
std::ostringstream oss; \
oss << __FILE__ << ':' << __LINE __ \
<< " CHECK FAILED: " << #CONDITION \
<< "; " << MESSAGE; \
throw std::runtime_error(oss.str()); \
} while (false)
...
for (int c = 0; c < M; ++c) {
if (c)
CHECK(file >> expected_semicolon &&
expected_semicolon == ';',
"values should be separated by semicolons");
CHECK(file >> *(A + N*c + r), "expected a numeric value");
}
For this specific input parsing, for a production system you might want to use getline so you can know where you are in the input...
size_t lineNum = 0;
std::string my_string;
for (int r = 0; r < N; ++r) {
CHECK(getline(file, my_string), "unexpect EOF in input");
++lineNum;
std::istringstream iss(my_string);
for (int c = 0; c < M; ++c) {
if (c)
CHECK(file >> expected_semicolon &&
expected_semicolon == ';',
"unexpected char '" << c
<< "' when semicolon separator needed on line "
<< lineNum);
CHECK(iss >> *(A + N*c + r),
"non numeric value encountered on line " << lineNum);
}
}
}

You should remove semicolon before converting
std::string temp;
file >> temp;
std::replace( temp.begin(), temp.end(), ';', ' ');
*(A + N*c + r) = std::stod( temp );

Why don't you try getline(), which accepts a Delimiter as 3rd argument.
string buffer;
for (int c = 0; c < M; ++c) {
getline(file, buffer, ';');
stringstream tmp(buffer);
tmp>>*(A + N*c + r);
}
getline() will read until the next delimiter or newline or end of file

Related

what can I get sum that string tokens converted int in C++?

I need to sum 100, 200, 300 in a.txt
a.txt
2323|A|5|0|2|100
2424|B|6|1|3|200
2525|C|7|2|4|300
so I opened this file, and read line by line using getline(), and tokenized.
main.cpp
for (std::string each; std::getline(split, each, split_char); tokens.push_back(each)) {
for (int i = 0; i < tokens.size(); i++) {
std::cout << tokens[i] << std::endl;
tokens.pop_back();
}
}
As expected, that code printed singly of all things.
so I thought using token index to sum values. but my code have error.
"vector subscript out of range" or no compile.
first try
for (std::string each; std::getline(split, each, split_char); tokens.push_back(each)) {
for (int i = 0; i < tokens.size(); i++) {
std::cout << tokens[i] << std::endl;
tokens.pop_back();
std::cout << tokens[5] << std::endl;
std::cout << tokens[11] << std::endl;
std::cout << tokens[17] << std::endl;
int a = 0;
int b = 0;
int c = 0;
int sum = 0;
a = stoi(tokens[5]);
b = stoi(tokens[11]);
c = stoi(tokens[17]);
sum = (a + b + c);
std::cout << sum << std::endl;
}
}
second try
for (std::string each; std::getline(split, each, split_char); tokens.push_back(each)) {
if(tokens.size() > 4) {
for (int k = 0; k < ((tokens.size() - 5) / 6) + 1; k++) {
int sum = 0;
int change = 0;
int j = 0;
j = 6 * k + 5;
change = stoi(tokens[j]);
sum += change;
std::cout << sum << std::endl;
tokens.pop_back();
}
}
}
what should I do sum value? and I'm wondering that tokens.size()`s meaning except meaning "size" because second for statement always get an error if not exactly correcting i < tokens.size()
You are modifying the tokens vector while you are looping through it. Don't do that. You are affecting its size(), which accounts for why you are able to go out of bounds.
You say that you need to sum only the last token of each line. But that is not what your code is trying to do. There is no need for an inner for loop at all. Simply split each line into a local tokens vector and then use tokens.back() to get the last token, eg:
std::string line;
int sum = 0;
while (std::getline(inFile, line))
{
std::istringstream iss(line);
std::vector<std::string> tokens;
std::string token;
while (std::getline(iss, token, '|')) {
tokens.push_back(token);
}
// use tokens as needed...
token = tokens.back();
sum += std::stoi(token);
}
std::cout << sum << std::endl;
Live Demo
I would like to structure my code slightly differently.
Rather than try and do everything in the main function split your code up so that you read each line and validate it is correct:
#include <iostream>
#include <string>
// A structure representing the data we want to parse.
struct DataLine
{
int v1;
char c;
int v2;
int v3;
int v4;
int v5;
// An input operator that will read one line of data.
// If the data is valid will update the variable we are reading into.
friend std::istream& operator>>(std::istream& str, DataLine& data)
{
DataLine tmp;
char s[5];
std::string extra;
if ( str >> tmp.v1 >> s[0] && s[0] == '|'
&& str >> tmp.c >> s[1] && s[1] == '|'
&& str >> tmp.v2 >> s[2] && s[2] == '|'
&& str >> tmp.v3 >> s[3] && s[3] == '|'
&& str >> tmp.v4 >> s[4] && s[4] == '|'
&& str >> tmp.v5
&& std::getline(str, extra) && extra.empty())
{
// all the data was read and the line was valid.
// update the correct variable.
swap(tmp, data);
}
else {
// there was an issue.
// set the stream to bad so that reading will stop.
str.setstate(std::ios::badbit);
}
return str;
}
// Standard swap method.
friend void swap(DataLine& lhs, DataLine& rhs) noexcept
{
using std::swap;
swap(lhs.v1, rhs.v1);
swap(lhs.c , rhs.c );
swap(lhs.v2, rhs.v2);
swap(lhs.v3, rhs.v3);
swap(lhs.v4, rhs.v4);
swap(lhs.v5, rhs.v5);
}
};
Then the loop you use to read the data becomes really trivial to implement.
int main()
{
DataLine data;
int sum = 0;
// Now we can read the data in a simple loop.
while(std::cin >> data) {
sum += data.v5;
}
std::cout << "Sum: " << sum << "\n";
}

Issues moving between files (&syntax), comparing structs, and c-strings

Prompt part 1
prompt part 2
The requirements of this program are in the photos. Please note I can only use the libraries i already have implemented in my code.
I have been trying all day and I know this is very messy and not the best way but I cannot think of any other ways to get what I want. (Please don't be too harsh on me, I am very well aware im not very smart. That's why im asking you)
Here is what I have
#include <iostream>
#include<fstream>
#include <cstring>
using namespace std;
struct Move{
char name[50];
int damage;
};
struct Character{
char name[50];
char char_class[50];
int hitPoints;
int armorClass;
bool isAlive;
char moveName[50];
};
struct Sesh{
char char1[50];
char moveName[50];
char target[50];
int roll;
};
void session( Character c, ofstream& out);
int main() {
Character c;
int M, N, K; //# of moves, # of characters
Character *character;
Move *moves;
Sesh *sesh;
/* To open an input file, that is, a file that contains data we need to
* read into our program, a la cin, we need a variable of type ifstream
*/
ifstream in1, in2;
// The next step is to open the input file using the ifstream variable
in1("character".c_str());
// If, for some reason, we cannot open the file, print an error message
if (!in1) {
cout << "Error opening character.txt" << endl;
} else {
// file was opened successfully.
// We can now use the ifstream variable just like cin, to read from the file
//Read in number of moves
in1 >> M;
//declare the dynamic Move array of size 'M'
moves = new Move[M];
in1.ignore();// to get rid of preceding newline
for (int i = 0; i < M; i++) {
char tab; // a char to read the separator character
getline(in1, moves[i].name, '\t');
in1 >> moves[i].damage; //do i need a \n here
in1 >> '\n';
}
in1 >> N;
character = new Character[N];
in1.ignore();
for (int i = 0; i < N; i++) {
char tab; // a char to read the separator character
getline(in1, character[i].name, '\t');
getline(in1, character[i].char_class, '\t');
in1 >> character[i].hitPoints;
in1 >> tab;
in1 >> character[i].armorClass;
in1 >> tab;
getline(in1, character[i].moveName, '\n');
// isAlive=true;
}
cout << "Inputs read in successfully" << endl;
}
in2("session".c_str());
if (!in2) {
cout << "Error opening session.txt" << endl;
} else {
in2 >> K; //numAttacks
sesh = new Sesh[K];
in2.ignore();
for (k = 0; k < 15; k++) { // k<K keep getting error for infinity loop?
char tab; // a char to read the separator character
getline(in2, character[k].name, '\t');
getline(in2, sesh[k].moveName, '\t');
getline(in2, sesh[k].target, '\t');
in2 >> sesh[k].roll;
in2 >> '\n';
}
for (i = 0; i < 9; i++) {
for (k = 0; k < 15; k++) {
if ((sesh[k].char1 == character[i].name) && (sesh[k].moveName == character[i].moveName)) {
for (int j = 0; j < N; j++) {
if (character[j].name == sesh[k].target) {
if (character[j].armorClass <= sesh[k].roll) {
for (int x = 0; x < M; x++) {
if (moves[x].name == character[i].moveName) {
character[j].hitPoints = character[j].hitPoints - moves[x].damage;
if (character[j].hitPoints < 0) {
character[j].isAlive = false;
}
}
}
}
}
}
}
}
}
ofstream out;
out(alive.c_string);
if (!out)
cout << "error opening MovieOutputs.txt" << endl;
else {
//output file was opened successfully
// We can use the ofstream variable just like cout, to print to the file
for (int i = 0; i < N; i++)
if (character[i].isAlive == 0) { //0 is true?
print(character[i], out); // Use the print function to print
cout << "Wrote to output file";
}
}
// once we are done with the file, we close it.
in1.close();
in2.close();
}
// We're done with the output file. Close it.
out.close();
//delete the dynamic array
delete[] character;
delete[] moves;
delete[] sesh;
// To run programs with files, we need to run using debugging.
// The cin.get() makes the program wait for us to press a key before it terminates.
cin.get();
}
void print(Character c, ofstream &out)
{
// Just use the oftstream variable like cout.
out << c.name << c.hitPoints << endl;
}

Program not exiting for loop after returning?

This program is suppose to identify tokens from an input file. Here is some of the input file
Test = Exam
The program should read in a line of text and identify the separate "phrases" that appear. For example the first line would produce the output "Test" -> IDENTIFIER, "=" -> ASSIGNMENT OPERATOR, "Exam" -> IDENTIFIER. Each line is a separate input to be read. I have a for loop to check the characters in the string to see what type of identifier it might be. For some reason, my program will not exit the for loop in my token type function. I have tried debugging but I am extremely confused where the program is getting stuck. I threw some cout statements in the program to maybe help identify the issue. For some reason the input is not getting past the first word "Test". It will read it in 3-5 times and then just error out.
Here is the current output (The program wouldn't normally output each individual character and index #, I just included it to make it possibly easier to follow along and locate the error)
File Opened
Test = Exam
0 T e s t
1 T e s t
2 T e s t
3 T e s t
4
5
6
7 T e s t
E x a m
#include <iostream>
#include <iomanip>
#include <string>
#include <fstream>
using namespace std;
const int SIZE = 100;
enum Token { IDENTIFIER, ARITHMETIC, RELATIONAL, LOGICAL, COMPOUND,
ASSIGNMENT, INTEGER, ILLEGAL, ENDLINE };
void getData(ifstream& inData, char data[]);
Token getTokenType(char data[], Token type[], char subStr[][SIZE], int index);
void displayData(char data[], Token type[], char subStr[][SIZE]);
void displayType(Token type[], int index);
int main()
{
char input[SIZE], subString[SIZE][SIZE];
Token type[SIZE];
ifstream inFile;
ofstream outFile;
inFile.open("mp5in.txt");
if (inFile)
cout << "File Opened" << endl << endl;
else
cout << "File Failed to Open" << endl << endl;
getData(inFile, input);
while (inFile)
{
for (int i = 0; type[i] != ENDLINE; i++)
{
cout << i << " ";
type[i] = getTokenType(input, type, subString, i);
cout << endl;
}
displayData(input, type, subString);
getData(inFile, input);
}
inFile.close();
return 0;
}
void getData(ifstream& inData, char data[])
{
int i = 0;
while (inData && data[i] != '\n')
{
inData.get(data[i]);
cout << data[i];
if (data[i] == '\n')
break;
i++;
}
}
Token getTokenType(char data[], Token type[], char subStr[][SIZE], int index)
{
int size = index;
int row = index, col = index;
bool valid = true, end = false;
for (int i = index; data[i] != '\n'; i += size)
{
if (isalpha(data[col]))
{
for (col = 0; subStr[row][col] != ' '; col++)
{
subStr[row][col] = data[col];
cout << subStr[row][col] << " ";
if ((!isalpha(subStr[row][col]) || !isdigit(subStr[row][col])) &&
subStr[row][col] != '_')
valid = false;
if (subStr[row][col] == ' ' && !valid)
return ILLEGAL;
else if (subStr [row][col] == ' ' && valid)
return IDENTIFIER;
}
}
}
}
Not all the program is here. I left it out as the program does not currently get that far along in the program.

How can I extract pairs of values from a string in C++

I have a string with this format:
"name1":1234 " name2 " : 23456 "name3" : 12345
and so on...
I have tried using nested while loops and two integers to store the position and length to use in string::substr, but I can't find a proper way to get it (most of the time I end up out of the string).
The values don't need to be stored, because I can call a function to process them as soon as I get them.
This is what I've done so far:
void SomeClass::processProducts(std::string str) {
unsigned int i = 0;
std::string name;
while (i < str.length()) {
if (str[i] == '\"') {
int j = 1;
while (str[i + j] != '\"') {
j++;
}
name = str.substr(i + 1, j - 1);
i += j;
}
else if (str[i] >= '0' && str[i] <= '9') {
int j = 1;
while (str[i + j] >= '0' && str[i + j] <= '9') {
j++;
}
//This is just processes the values
std::stringstream ss;
std::string num = str.substr(i, j);
ss.str(num);
int products = 0;
ss >> products;
if (products == 0) {
Util::error(ERR_WRONG_PRODUCTS);
}
int pos = getFieldPos(name);
if (pos == -1) {
Util::error(ERR_WRONG_NAME);
}
else {
fields[pos].addProducts(products);
}
i += j;
}
i++;
}
}
Thanks in advance.
Unfortunately, C++ doesn't have strong string parsing abilities out of the box. That's why there are many ways of doing these kinds of tasks.
However, there C++ does provide tools to help. So we can use them and at least avoid manual loops.
Before we begin I want to draw attention to the fact that when we are dealing with user input we must take extra care to validate the input.
The blocks that we need for the solution I chose are:
matching the format (with "name" : value). For this I chose std::find. Regex can also be used.
parsing the value into a number. For this we could use std::stoi. See bellow why it's not enough.
always make sure we are getting the input we expect. This adds some boilerplate code, but that's the price we have to pay. Also here we have a problem with std::stoi as it happily accepts trailing non whitespace without a fuss. So for instance 123 invalid would be parsed to 123. This is the reason I use a small wrapper around it parse_string_to_int
Ok, on we go:
Little helper:
auto parse_string_to_int(const std::string& str)
{
std::size_t num_processed = 0;
int val = std::stoi(str, &num_processed, 10);
auto next_non_space = std::find_if(str.begin() + num_processed, str.end(),
[](char ch) { return !std::isspace(ch); });
if (next_non_space != str.end())
throw std::invalid_argument{"extra trailing characters in parse_string_to_int"};
return val;
}
struct Product_token
{
std::string name;
int value;
};
auto get_next_product(std::string::const_iterator& begin, std::string::const_iterator end)
-> Product_token
{
// match `"name" : value "`
auto name_open_quote = std::find(begin, end, '\"');
auto name_close_quote = std::find(name_open_quote + 1, end, '\"');
auto colon = std::find(name_close_quote, end, ':');
auto next_token_open_quote = std::find(colon, end, '\"');
if (name_close_quote == end || name_close_quote == end || colon == end)
{
// feel free to add more information regarding the error.
// this is just the bare minimum to accept/reject the input
throw std::invalid_argument{"syntax error on parsing product"};
}
// advance to next token
begin = next_token_open_quote;
return Product_token{{name_open_quote + 1, name_close_quote},
parse_string_to_int({colon + 1, next_token_open_quote})};
}
auto process_products(const std::string& str)
{
auto begin = str.begin();
while (begin != str.end())
{
auto product = get_next_product(begin, str.end());
cout << '"' << product.name << "\" = " << product.value << endl;
}
}
int main()
{
auto str = R"("name1":1234 " name2 " : 23456 "name3" : 12345)"s;
try
{
process_products(str);
}
catch (std::exception& e)
{
cerr << e.what() << endl;
}
}
See full code in action on ideone
As long as you know the format, then extracting data is rather easy. First remove any quote or colon from the string and replace them with space. Now the string is delimited by space.
#include <iostream>
#include <iterator>
#include <string>
#include <algorithm>
#include <vector>
#include <sstream>
using namespace std;
int main()
{
string str("\"name1\":1234 \" name2 \" : 23456 \"name3\" : 12345");
cout << str << endl;
// remove ':' and '"' and replace them by space
std::replace_if(str.begin(), str.end(), ispunct, ' ');
istringstream ss(str);
vector<string> words;
// store data as name and number in vector<string>
copy(istream_iterator<string>(ss),istream_iterator<string>(),back_inserter(words));
for (int i(0); i < words.size(); i+=2)
cout << "name: " << words[i] << " number: " << words[i+1] << endl;
return 0;
}
The result is
"name1":1234 " name2 " : 23456 "name3" : 12345
name: name1 number: 1234
name: name2 number: 23456
name: name3 number: 12345

C++ insertion sort from a txt file

I have to read from a .txt file and out it with a different .txt file. I have to use insertion sort in order to sort them based on two numbers. I could only get this far, I don't know how to do insertion sort in this program where I have two numbers to sort to.
Here is my code:
#include <iostream>
#include <fstream>
#include <string>
using namespace std;
int main(void)
{
int serialno[100], suratno[100], ayatno[100];
string order;
string str;
char ch;
int i = 0;
int j, temp;
ifstream fin;
fin.open("text.txt");
if(!fin)
{
cout << "Cannot open file \'text.txt\'! Quitting.\n";
exit(0);
}
while(fin)
{
fin.get(ch); //gets .
getline(fin, order, '('); //allegedly it removes the delimiter char from stream too
fin >> suratno;
fin.get(ch); //gets :
fin >> ayatno;
fin.get(ch); //gets )
fin.get(ch); //gets \n
cout << serialno << "." << order << "("<<suratno<<":<<ayatno<<")\n";
}
fin.close();
//sort algorithm
for (int i = 0; i < length; i++){
j = i;
while (j > 0 && suratno [j] < suratno [j-1]){
temp = suratno [j];
suratno [j] = suratno [j-1];
suratno [j-1] = temp;
j--;
cout << serialno << endl;
}
}
}
ofstream fout;
fout.open("newtext.txt");
if(!fout)
{
cout << "Cannot open output file\'orderedquranorders.txt\'!Quitting.\n";
exit(0);
}
i = 0;
//write sorted list to output file
fout.close();
cout << i << " orders successfully sorted and written.\n";
}
this is the text file (numbers in bracket should be used, firstly with number before colon, and secondly with number after colon):
1. Do not be rude in speech (3:159)
2. Restrain Anger (3:134)
3. Be good to others (4:36)
4. Do not be arrogant (7:13)
5. Forgive others for their mistakes (7:199)
6. Speak to people mildly (20:44)
7. Lower your voice (31:19)
8. Do not ridicule others (49:11)
9. Be dutiful to parents(17:23)
current output:
Do not be rude in speech (3:159)
Restrain Anger (3:134)
Be good to others (4:36)
Be dutiful to parents(17:23)
expected output:
Restrain Anger (3:134)
Do not be rude in speech (3:159)
Be good to others (4:36)
Be dutiful to parents(17:23)
sorted in terms of both the numbers and the serial no stays the same
In order to compare two pair of numbers, you can make comparisons like:
if(suratno[i] < suratno[i-1] || (suratno[i] == suratno[i-1] && ayatno[i] < ayatno[i-1])){
/* swap */
}
Or you can use one expression: expr = suratno * 10000 + ayatno. And make just one comparison:
if(expr[i] < expr[i-1]){
/* swap */
}
Also, I have a few observations about your algorithm/code:
Don't use using namespace std. Specially in big programs, because it can cause obscure bugs (see an example here). Instead use using std::<name> when you want to avoid std::. Ex. using std::string. In general, avoid using namespace xxxx.
I see you did parse the input lines manually, I prefer to use regular expressions, that are much more versatile and powerful, but requires a little learning.
When it's necessary to write an error message, always write to stderr stream cerr in C++.
In the sort algorithm, it's better start in 1 than 0, because the first item doesn't have a previous item to compare with.
Finally the swap can be done with an existent C++ function.
Here is your code reorganized and using regular expressions that I tried to explain as much as possible:
#include <iostream>
#include <fstream>
#include <string>
#include <regex>
#include <vector>
#include <algorithm>
using std::string;
struct Line {
int expr; // Expression used to compare
string text; // Original line without initial number
};
int main() {
std::regex linePattern(
"\\d+" // 1 or more digits
"\\. " // '. ' (dot followed by 1 space)
"(" // begin char group #1
".*" // zero or more chars
"\\(" // '(' (left parenthesis)
"(\\d+)" // char group #2 (suratno: 1+ digits)
":" // ':' (colon)
"(\\d+)" // char group #3 (ayatno: 1+ digits)
"\\)" // ')' (right parenthesis)
")" // end char group #1
);
std::smatch groups; // Regular expression found char groups
std::vector<Line> lines; // Vector to store the readed lines
// Read lines parsing content
std::ifstream fin("text.txt");
if(!fin){
std::cerr << "Cannot open file 'text.txt'! Quitting.\n";
return 1;
}
string line;
while (std::getline(fin, line))
if (std::regex_search(line, groups, linePattern) && groups.size() > 0) {
int suratno = std::stoi(groups[2]);
int ayatno = std::stoi(groups[3]);
int compExpr = suratno * 10000 + ayatno; // assumes ayatno < 10,000
lines.push_back({ compExpr, groups[1] });
}
fin.close();
// sort algorithm (better start in 1)
for (size_t i = 1; i < lines.size(); i++)
for (size_t j = i; j > 0 && lines[j].expr < lines[j - 1].expr; j--)
std::swap(lines[j], lines[j - 1]);
std::ofstream fout("newtext.txt");
if(!fout){
std::cerr << "Cannot open output file 'orderedquranorders.txt'! Quitting.\n";
return 1;
}
for (size_t i = 0; i < lines.size(); i++)
fout << i + 1 << ". " << lines[i].text << std::endl;
fout.close();
std::cout << lines.size() << " orders successfully sorted and written.\n";
return 0;
}
Note: The regular expression is really one string "\\d+\\. (.*\\((\\d+):(\\d+)\\))", I used a C/C++ feature that concatenates strings separated by spaces before compilation, so the compiler sees only one string.
Don't forget to compile with -std=c++11 option.
using namespace std; is considered bad practice and can be dangerous sometimes. Check this
Here is your solution:
#include <iostream>
#include <fstream>
#include <string>
int main()
{
int suratno[100], ayatno[100];
std::string order[100];
char ch;
int count = 0;
int tempInt;
std::string tempStr;
std::ifstream fin;
fin.open("text.txt");
if (!fin)
{
std::cout << "Cannot open file \'text.txt\'! Quitting.\n";
exit(0);
}
else
{
while (fin)
{
fin.get(ch); //gets the numbers
fin.get(ch); //gets .
getline(fin, order[count], '('); //allegedly it removes the delimiter char from stream too
fin >> suratno[count];
fin.get(ch); //gets :
fin >> ayatno[count];
fin.get(ch); //gets )
fin.get(ch); //gets \n
std::cout << count + 1 << "." << order[count] << "(" << suratno[count] << ":" << ayatno[count] << ")\n";
count++;
}
}
fin.close();
std::cout << std::endl;
// sort algorithm (we must sort two times)
for (int i = 0; i < count; i++)
{
for (int j = i; j > 0 && suratno[j] < suratno[j - 1]; j--)
{
tempInt = suratno[j];
suratno[j] = suratno[j - 1];
suratno[j - 1] = tempInt;
tempInt = ayatno[j];
ayatno[j] = ayatno[j - 1];
ayatno[j - 1] = tempInt;
tempStr = order[j];
order[j] = order[j - 1];
order[j - 1] = tempStr;
}
}
for (int i = 0; i < count; i++)
{
for (int j = i; j > 0 && suratno[j] == suratno[j - 1] && ayatno[j] < ayatno[j - 1]; j--)
{
tempInt = ayatno[j];
ayatno[j] = ayatno[j - 1];
ayatno[j - 1] = tempInt;
tempInt = suratno[j];
suratno[j] = suratno[j - 1];
suratno[j - 1] = tempInt;
tempStr = order[j];
order[j] = order[j - 1];
order[j - 1] = tempStr;
}
}
// print the sorted list just to check
for (int i = 0; i < count; i++)
{
std::cout << i + 1 << "." << order[i] << "(" << suratno[i] << ":" << ayatno[i] << ")\n";
}
// write sorted list to output file
std::ofstream fout;
fout.open("newtext.txt");
if (!fout)
{
std::cout << "Cannot open output file\'orderedquranorders.txt\'!Quitting.\n";
exit(0);
}
else
{
for (int i = 0; i < count; i++)
{
fout << i + 1 << "." << order[i] << "(" << suratno[i] << ":" << ayatno[i] << ")\n";
}
}
fout.close();
std::cout << std::endl;
std::cout << count << " orders successfully sorted and written.\n";
return 0;
}