Note: I'm rather a c++ rookie.
I've the following problem:
I read from a file and want to replace string parts with other string parts. Simple task, should be easy to do. I did some experiments with setmode, but without success. I googled also imbue, but it didn't help me either.
There might be minor code issues, but please focus on the problem. The test data for conversion is given below:
int main(int argc, char** argv) {
stringstream strs;
//Choose output file
ofstream ofile;
string filename;
cout << "Ausgabepfad eingeben" << endl;
cin >> filename;
ofile.open(&filename[0]);
//Choose input file
ifstream ifile;
cout << "Quellpfad eingeben" << endl;
cin >> filename;
ifile.open(&filename[0]);
//Choose decoding - doesnt work, I know - That was for experiments
int mode = 0;
cout << "Decoding wählen\n1 für _O_TEXT\n2 für _O_BINARY\n3 für _O_U16TEXT\n4 für _O_U8TEXT\n5 für _O_WTEXT" << endl;
cin >> mode;
/* switch(mode){
case 1:
_setmode (_fileno(ifstream), _O_TEXT);
break;
case 2:
_setmode (_fileno(ifstream), _O_BINARY);
break;
case 3:
_setmode (_fileno(ifstream), _O_U16TEXT);
break;
case 4:
_setmode (_fileno(ifstream), _O_U8TEXT);
break;
case 5:
_setmode (_fileno(ifstream), _O_WTEXT);
break;
default:
cerr << "ungültige Codierung gewählt"
}
*/
//Choose search string and replacement string
ifile.seekg(0); //not necessary, I know
string searchstr = "";
cout << "Suchstring eingeben" << endl;
cin >> searchstr;
string fillstr;
cout << "Ersetzungsstring eingeben" << endl;
cin >> fillstr;
cout << fillstr;
int marker = 0;
if(searchstr.length()<1){
return 0;
}
//actual program
while(!ifile.eof()){
int counter = 0;
ifile.seekg(marker);
char current = ifile.get();
if(current==searchstr[0]){ //if the first search letter matches, seet, if the others do.
marker++;
counter++;
for(int i = 1; i < searchstr.length(); i++){
ifile.seekg(marker);
if(ifile.get()==searchstr[i]){
counter++;
marker++;
}else{
marker-i;
break;
}
}
if(counter == searchstr.length()){
ofile << fillstr;
cout << endl;
}
}else{
ofile << ifile.get();
cout << ifile.get();
}
marker++;
}
ifile.close();
ofile.close();
cout << endl;
return 1;
}
The following string
|-
should be replaced by
\n
The text (sample) is:
|[[Allgemeines Deutsches Kommersbuch:1|1]]
|1
|[[Abend wird's, des Tages (Körner)]]
|[[Die Eichen (Körner)]]
|-
|[[Allgemeines Deutsches Kommersbuch:2|2]]
|2
The program runs, but it doesn't work. The output is lots of numbers without any sense.
The output is lots of numbers without any sense.
The call ifile.get() returns a value of type int_type. Thus << outputs all these integer values.
Replace
ofile << ifile.get();
(doing formatted output of integers) with
ofile << char( ifile.get() );
or (preferred, using unformatted input and output)
ofile.put( ifile.get() );
It's almost always a better idea to express the problem in terms of standard streams, iterators and algorithms:
#include <iostream>
#include <string>
#include <sstream>
#include <algorithm>
// lets get the logic right in terms of streams
void replace_all(std::ostream& dest, std::istream& source,
const std::string& search_for, const std::string& replace_with)
{
std::string line;
while (std::getline(source, line)) {
auto ipos = line.begin();
while (ipos != line.end())
{
ipos = std::search(ipos, line.end(), search_for.begin(),
search_for.end());
if (ipos != line.end())
{
auto start_index = std::distance(line.begin(), ipos);
line.replace(ipos, std::next(ipos, search_for.length()),
replace_with);
ipos = std::next(line.begin() + start_index + replace_with.length());
}
}
dest.write(line.data(), line.size());
dest.put('\n');
}
}
// now test
int main()
{
std::string search_for = "|-";
std::string replace_with = "";
std::istringstream source_stream("|[[Allgemeines Deutsches Kommersbuch:1|1]]\n"
"|1\n"
"|[[Abend wird's, des Tages (Körner)]]\n"
"|[[Die Eichen (Körner)]]\n"
"|-\n"
"|[[Allgemeines Deutsches Kommersbuch:2|2]]\n"
"|2\n");
std::ostringstream dest_stream;
replace_all(dest_stream, source_stream, search_for, replace_with);
std::cout << dest_stream.str();
return 0;
}
// todo: write a new main which asks for filenames, builds ifstreams and ofstreams, and calls replace_all()
expected output:
|[[Allgemeines Deutsches Kommersbuch:2|2]]
|2
Jewels-MacBook-Pro:play richardh$ ./replace.cpp
|[[Allgemeines Deutsches Kommersbuch:1|1]]
|1
|[[Abend wird's, des Tages (Körner)]]
|[[Die Eichen (Körner)]]
|[[Allgemeines Deutsches Kommersbuch:2|2]]
|2
Related
I'm working on a code that reads in a C++ source file and converts all ‘<’ symbols to “<” and all ‘>’ symbols to “>”. I wrote out the main method and everything compiled nicely but now that I'm actually writing out my convert function at the top of the program, I'm stuck in an infinite loop and I'm hitting a wall on what the culprit is. Could someone help me out?
I included the whole program in case the problem lies in my I/O coding but I surrounded the function with slashes. Hopefully I won't get flamed.
#include <iostream>
#include <fstream>
#include <cstdlib>
#include <string>
#include <cstring>
using namespace std;
//FUNCTION GOES THROUGH EACH CHARACTER OF FILE
//AND CONVERTS ALL < & > TO < or > RESPECTIVELY
//////////////THIS IS THE FUNCTION IN QUESTION//////////
void convert (ifstream& inStream, ofstream& outStream){
cout << "start" << endl;
char x;
inStream.get(x);
while (!inStream.eof()){
if (x == '<')
outStream << "<";
else if (x == '>')
outStream << ">";
else
outStream << x;
}
cout << "end" << endl;
};
///////////////////////////////////////////////////////////////////////////
int main(){
//FILE OBJECTS
ifstream inputStream;
ofstream outputStream;
string fileName;
//string outFile;
//USER PROMPT FOR NAME OF FILE
cout << "Please enter the name of the file to be converted: " << endl;
cin >> fileName;
//outFile = fileName + ".html";
//ASSOCIATES FILE OBJECTS WITH FILES
inputStream.open(fileName.c_str());
outputStream.open(fileName + ".html");
//CREATES A CONVERTED OUTPUT WITH <PRE> AT START AND </PRE> AT END
outputStream << " <PRE>" << endl;
convert(inputStream, outputStream);
outputStream << " </PRE>" << endl;
inputStream.close();
outputStream.close();
cout << "Conversion complete." << endl;
return 0;
}
It isn't a good approach to manipulate a file while you're reading it. The right way is, first read the whole file, store the data, manipulate the stored data, and then update the file. Hope this code will help you :)
void convert()
{
int countLines = 0; // To count total lines in file
string *lines; // To store all lines
string temp;
ifstream in;
ofstream out;
// Opening file to count Lines
in.open("filename.txt");
while (!in.eof())
{
getline(in, temp);
countLines++;
}
in.close();
// Allocating Memory
lines = new string[countLines];
// Open it again to stroe data
in.open("filename.txt");
int i = 0;
while (!in.eof())
{
getline(in, lines[i]);
// To check if there is '<' symbol in the following line
for (int j = 0; lines[i][j] != '\0'; j++)
{
// Checking the conditon
if (lines[i][j] == '<')
lines[i][j] = '>';
}
i++;
}
in.close();
// Now mainuplating the file
out.open("filename.txt");
for (int i = 0; i < countLines; i++)
{
out << lines[i];
if (i < countLines - 1)
out << endl;
}
out.close();
}
I'm using a simple encryption that I found online. Basically, I'm streaming in a file, checking to see if that file is open (if not, display an error message) and putting each line in each element of the array while encrypting the information. Afterwards I stream that encrypted information onto an output file.
However, I'm getting nothing in my output.txt file. The encryption works fine if you test it by itself.
Here is my code:
#include <string>
#include <fstream>
#include <sstream> // for ostringstream
#include <iostream>
#include <stdio.h>
#include <algorithm>
/* Credits to kylewbanks.com */
string encrypt (string content) {
char key[3] = {'K'}; //Any chars will work
string output = content;
for (int i = 0; i < content.size(); i++)
output[i] = content[i] ^ key[i % (sizeof(key) / sizeof(char))];
return output;
}
int main() {
string input, line;
string content[10000];
string encryptedContent[10000];
int counter = 0, innerChoice = 0, i, finalCounter;
cout << "\tPlease enter the file name to encrypt!\n";
cout << "\tType '0' to get back to the menu!\n";
cout << "Input >> ";
cin >> input;
/* Reads in the inputted file */
ifstream file(input.c_str());
//fopen, fscanf
if(file.is_open()) {
/* Counts number of lines in file */
while (getline(file, line)) {
counter++;
}
cout << counter;
finalCounter = counter;
for (i = 0; i < finalCounter; i++) {
file >> content[i];
encryptedContent[i] = encrypt(content[i]);
cout << encryptedContent[i];
}
} else {
cout << "\tUnable to open the file: " << input << "!\n";
}
/* Write encryption to file */
ofstream outputFile("output.txt");
for (i = 0; i < finalCounter ; i++) {
outputFile << encryptedContent;
}
outputFile.close();
}
Any clue what is wrong?
string content[10000];
string encryptedContent[10000];
This is wrong because it is creating 20000 strings (you probably think it is creating a large enough character array to read the data).
string content; is enough. It can be resized to handle any length of strings.
You just need to read/write the file in binary:
int main()
{
string input = "input.txt";
ifstream file(input, ios::binary);
if (!file.is_open())
{
cout << "\tUnable to open the file: " << input << "!\n";
return 0;
}
string plaintext;
//read the file
file.seekg(0, std::ios::end);
size_t size = (size_t)file.tellg();
file.seekg(0);
plaintext.resize(size, 0);
file.read(&plaintext[0], size);
cout << "reading:\n" << plaintext << "\n";
//encrypt the content
string encrypted = encrypt(plaintext);
//encrypt again so it goes back to original (for testing)
string decrypted = encrypt(encrypted);
cout << "testing:\n" << decrypted << "\n";
/* Write encryption to file */
ofstream outputFile("output.txt", ios::binary);
outputFile.write(encrypted.data(), encrypted.size());
return 0;
}
I have a file of format as:
2
3 4
7 8 9
10 20 22 02
...
basically numbers in each line , separated by spaces.
I have to read from the file, extract all numbers and maintain their line number too, as I have to make a tree later. I'm doing this to take input, but getting weird outputs.
#include<cstdio>
#include<iostream>
#include<cctype>
using namespace std;
void input()
{
char c,p;
while(c=getchar()!=EOF)
{
if(c=='\n') printf("},\n{");
else if(c==' ') printf(",");
else if(c=='0')
{
p=getchar();
if(p==' ')
{
printf("%c%c,",c,p);
}
else
{
printf("%c,",p);
}
}
else if(isalpha(c))
{
printf("%c",c);
}
}
}
int main()
{
input();
}
The image shows the input and output
You are writing more C than C++.
In C++ you can use streams. Use peek() to check the next character, and >> to actually read it.
E.g.:
using namespace std;
int main(){
ifstream s("/tmp/input");
int nr;
while (!s.eof()) {
switch (s.peek()){
case '\n': s.ignore(1); cout << "},\n{"; break;
case '\r': s.ignore(1); break;
case ' ': s.ignore(1); cout << ", "; break;
default: if (s >> nr) cout << nr;
}
}
}
Use a file stream, read line by line and parse each line with a stringstream:
std::ifstream file("filename");
std::string line;
size_t line_number(1);
while ( std::getline(file, line) ) // reads whole lines until no more lines available
{
std::stringstream stream(line);
int tmp;
std::cout << "Numbers in line " << line_number << ":";
while ( stream >> tmp ) // reads integer divided by any whitespace until no more integers available
{
std::cout << " " << tmp;
}
std::cout << "\n";
++line_number;
}
You'll need to include
#include <iostream> // for std::cout
#include <string> // for std::string
#include <fstream> // for std::ifstream
#include <sstream> // for std::stringstream
I have the following structure:
struct productInfo
{
int item;
string details;
double cost;
};
I have a file that will input 10 different products that each contain an item, details, and cost. I have tried to input it using inFile.getline but it just doesn't work. Can anyone give me an example of how to do this? I would appreciate it.
Edit
The file contains 10 lines that look like this:
570314,SanDisk Sansa Clip 8 GB MP3 Player Black,55.99
Can you provide an example please.
Edit
Sorry guys, I am new to C++ and I don't really understand the suggestions. This is what I have tried.
void readFile(ifstream & inFile, productInfo products[])
{
inFile.ignore(LINE_LEN,'\n'); // The first line is not needed
for (int index = 0; index < 10; index++)
{
inFile.getline(products[index].item,SIZE,DELIMETER);
inFile.getline(products[index].details,SIZE,DELIMETER);
inFile.getline(products[index].cost,SIZE,DELIMETER);
}
}
This is another approach that uses fstream to read the file and getline() to read each line on the file. The parsing of the line itself was left out on purpose since other posts have already done that.
After each line is read and parsed into a productInfo, the application stores it on a vector, so all products could be accessed in memory.
#include <iostream>
#include <fstream>
#include <vector>
#include <iterator>
#include <string>
using namespace std;
struct productInfo
{
int item;
string details;
double cost;
};
int main()
{
vector<productInfo> product_list;
ifstream InFile("list.txt");
if (!InFile)
{
cerr << "Couldn´t open input file" << endl;
return -1;
}
string line;
while (getline(InFile, line))
{ // from here on, check the post: How to parse complex string with C++ ?
// https://stackoverflow.com/questions/2073054/how-to-parse-complex-string-with-c
// to know how to break the string using comma ',' as a token
cout << line << endl;
// productInfo new_product;
// new_product.item =
// new_product.details =
// new_product.cost =
// product_list.push_back(new_product);
}
// Loop the list printing each item
// for (int i = 0; i < product_list.size(); i++)
// cout << "Item #" << i << " number:" << product_list[i].item <<
// " details:" << product_list[i].details <<
// " cost:" << product_list[i].cost << endl;
}
EDIT: I decided to take a shot at parsing the line and wrote the code below. Some C++ folks might not like the strtok() method of handling things but there it is.
string line;
while (getline(InFile, line))
{
if (line.empty())
break;
//cout << "***** Parsing: " << line << " *****" << endl;
productInfo new_product;
// My favorite parsing method: strtok()
char *tmp = strtok(const_cast<char*>(line.c_str()), ",");
stringstream ss_item(tmp);
ss_item >> new_product.item;
//cout << "item: " << tmp << endl;
//cout << "item: " << new_product.item << endl;
tmp = strtok(NULL, ",");
new_product.details += tmp;
//cout << "details: " << tmp << endl;
//cout << "details: " << new_product.details << endl;
tmp = strtok(NULL, " ");
stringstream ss_cost(tmp);
ss_cost >> new_product.cost;
//cout << "cost: " << tmp << endl;
//cout << "cost: " << new_product.cost << endl;
product_list.push_back(new_product);
}
It depends on what's in the file? If it's text, you can use the redirect operator on a file input stream:
int i;
infile >> i;
If it's binary, you can just read it in to &your_struct.
You have to
0) Create a new instance of productInfo, pinfo;
1) read text (using getline) to the first comma (','), convert this string to an int, and put it into pinfo.item.
2) read text to the next comma and put it into pinfo.details;
3) read text to the endline, convert the string to a double, and put it into pinfo.cost.
Then just keep doing this until you reach the end of the file.
Here is how I would use getline. Note that I use it once to read from the input file, and then again to chop that line at ",".
ostream& operator>>(istream& is, productInfo& pi)
{
string line;
getline(is, line); // fetch one line of input
stringstream sline(line);
string item;
getline(sline, item, ',');
stringstream(item) >> pi.item; // convert string to int
getline(sline, item, ',');
pi.details = item; // string: no conversion necessary
getline(sline, item);
stringstream(item) >> pi.cost; // convert string to double
return is;
}
// usage:
// productInfo pi; ifstream inFile ("inputfile.txt"); inFile >> pi;
N.b.: This program is buggy if the input is
99999,"The Best Knife, Ever!",16.95
I am using getline to read up to end of newline but c++ getline gets me stuff till space,
I have txt file data as
address(tab char)1420 Happy Lane
When I do
getline(reader, ss, '\t') I get address in ss string.
when I do getline(reader, ss, '\n') I just get 1420.
I want full "1420 Happy Lane", How to get it ?
Thanks.
#include <fstream>
#include <string>
#include <iostream>
#include <vector>
#include <sstream>
using namespace std;
int main( int argc, char *argv[] )
{
if( argc < 2 )
{
cout << "Missing filename as first argument" << "\n";
exit(2);
}
vector<string> myvector;
string ss;
int i=0, j=0;
ifstream reader(argv[1]);
if (! reader )
{
cout << "Error opening input file : " << " " << argv[1] << '\n';
return -1;
}
while( !reader.eof())
{
if ((i+1) % 2 == 0 )
getline(reader, ss, '\n');
else
getline(reader, ss, '\t');
if (ss[0] == '#')
{
//Skip
getline(reader,ss, '\n');i=0;
continue;
}
i++;
myvector.push_back(ss);
}
reader.close();
vector<string>::iterator it;
stringstream stream;
int vecloc=1;
string tag;
string sData;
cout << "myvector contains: \n";
for ( it=myvector.begin() ; it < myvector.end(); it++ )
{
switch (vecloc)
{
case 1: stream << *it; stream >> tag; vecloc++;break;
case 2:
stream << *it; stream >> sData;
// Do job
cout << tag << " " << sData << "\n";
// Reset.
vecloc=1; break;
default : break;
}
// Clear String stream
stream.str(""); stream.clear();
}
return(0);
}
output
/home/sr/utl
cat abc.txt
hey c++ making me nuts.
/home/sr/utl
a.out abc.txt
myvector contains:
hey c++
Paste the actual code from your editor and double check that there isn't a newline (or maybe other unexpected non-printing characters) in your data file.
This works as expected here:
#include <iostream>
#include <sstream>
using namespace std;
int main()
{
stringstream reader("address\t1420 Happy Lane\n");
string ss;
getline(reader, ss, '\t');
cout << "1: " << ss << endl;
getline(reader, ss, '\n');
cout << "2: " << ss << endl;
}
Output:
1: address
2: 1420 Happy Lane
I got a split() function you can use for that. Use \t as the delimeter:
void split(std::string &string, std::vector<std::string> &tokens, const char &delim) {
std::string ea;
std::stringstream stream(string);
while(getline(stream, ea, delim))
tokens.push_back(ea);
}
You're trying to alternate between grabbing up until a \t and grabbing up until a \n. But the times that you find a '#' comment line throw off your alternation.
By far the easiest and most robust way to handle this sort of thing is to read each line first, and then re-parse the line.