Why am I getting this out of range error - c++

I have a file that contains a set range of numbers for example 20. The contents of the file are social security numbers, I am trying to erase every '-'. Though while going through the file I am receiving the following error.
libc++abi.dylib: terminating with uncaught exception of type std::out_of_range: basic_string
Here is the code
string str;
ifstream inFile (fName);
//fName= name of the file
if (inFile.is_open())
{
while ( getline (inFile,str) )
{
cout << str << endl;
str.erase(3,1);//erasing first '-'
str.erase(5,1);//erasing second '-'
cout << str << endl;
}
inFile.close();
}
else
cout << "Unable to open file";

I suggest adding some checks to your code so you don't assume there are two '-'s where you expect to find them.
if ( str.size() >= 4 && str[3] == '-' )
{
str.erase(3,1);
if ( str.size() >= 6 && str[5] == '-' )
{
str.erase(5,1);
}
else
{
std::cout << "Did not find a '-' at the 6-th position of the string.\n";
std::cout << str << std::endl;
}
}
else
{
std::cout << "Did not find a '-' at the 4-th position of the string.\n";
std::cout << str << std::endl;
}

Related

Reading in fasta file C++

I'm trying to read in a fasta file. I want to remove/ignore the header/info lines that begin with ">" and store the following sequences into sperate strings. Below is the code I have to do that (partially reworked from https://rosettacode.org/wiki/FASTA_format#C++, as what I had originally worked even less). They have a good example of what I want to do.
My problem is given this fasta file:
">sequence_1
MSTAGKVIKCKAAVLWELHKPFTIEDIEVAPPKAHEVRIKMVATGVCRSDDHVVSGTLVTPLPAVLGHE
GAGIVEGVTCVKPGDKVIPLFSPQCGECRICKHPESNFCSRSDLLMPRGTLREGTSRFSCKGKQIHNFI
STSTFSQYTVVDDIAVAKIDGASPLDKVCLIGCGFSTGYGSAVKVAKVTPGSTCAVFGLGGVGLSVIIG
CKAAGAARIIAVDINKDKFAKAKELGATECIYSKPIQEVLQEMTDGGVDFSFEVIGRLDTMTSALLSCH
AACGVSVVVGVPPNAQNLSMNPMLLLLGRTWKGAIFGGFKSKDSVPKLVAKKFPLDPLITHVLPFEKIN
EAFDLLRSGKSIRTVLTF
">sequence_2
MNQGKVIKCKAAVLWEVKKPFSIEDVEVAPPKAYEVRIKMVAVGICHTDDHVVSGNLVTPLPVILGHEA
AGIVESVGEGVTTVKPGDKVIPLFTCRVCKNPESNYCLKNDLGNPRGTLQDGTRRFTCRGKPIHHFLGT
STFSQYTVVDENAVAKIDAASPLEKVCLIGCGFSTGYGSAVNVAKVTPGSTCAVFGLGGVGLSAVMGCK
AAGAARIIAVDINKDKFAKAKELGATECINPQDYKLPIQEVLKEMTDGSTVIGRLDTMMASLLCCGTSV
IVEDTPASQNLSINPMLLLTGRTWKGAVYGGFKSKEGIPKLVADFMAKKFSLDALITHVLPFEKINEGF
DLLHSGKSIRTVLTF
My output:
Sequence 1: MSTAGKVIKCKAAVLWELHKPFTIEDIEVAPPKAHEVRIKMVATGVCRSDDHVVSGTLVTPLPAVLGHEGAGIVEGVTCVKPGDKVIPLFSPQCGECRICKHPESNFCSRSDLLMPRGTLREGTSRFSCKGKQIHNFISTSTFSQYTVVDDIAVAKIDGASPLDKVCLIGCGFSTGYGSAVKVAKVTPGSTCAVFGLGGVGLSVIIGCKAAGAARIIAVDINKDKFAKAKELGATECIYSKPIQEVLQEMTDGGVDFSFEVIGRLDTMTSALLSCHAACGVSVVVGVPPNAQNLSMNPMLLLLGRTWKGAIFGGFKSKDSVPKLVAKKFPLDPLITHVLPFEKINEAFDLLRSGKSIRTVLTF
Sequence 2: MNQGKVIKCKAAVLWEVKKPFSIEDVEVAPPKAYEVRIKMVAVGICHTDDHVVSGNLVTPLPVILGHEAAGIVESVGEGVTTVKPGDKVIPLFTCRVCKNPESNYCLKNDLGNPRGTLQDGTRRFTCRGKPIHHFLGTSTFSQYTVVDENAVAKIDAASPLEKVCLIGCGFSTGYGSAVNVAKVTPGSTCAVFGLGGVGLSAVMGCKAAGAARIIAVDINKDKFAKAKELGATECINPQDYKLPIQEVLKEMTDGSTVIGRLDTMMASLLCCGTSVIVEDTPASQNLSINPMLLLTGRTWKGAVYGGFKSKEGIPKLVADFMAKKFSLDALITHVLPFEKINEGF
The last line or so of Sequence 2 is cut off..... Any help/solutions?
void read_in_Protein(string Protein_filename)
{ // read in the sequences
fstream myfile;
myfile.open(Protein_filename, ios::in);
if (!myfile.is_open()) {
cerr << "Error can not open file" << endl;
exit(1);
}
string Protein_Sequences{};
string Protein_Seq_names{};
// string temp{};
string Prot_Seq1{};
string Prot_Seq2{};
string line{};
while (getline(myfile, line).good()) {
//std::cout << "Line input received (" << line.length() << "): " << line << std::endl;
if (line.empty() || line[0] == '>') { // Identifier marker
if (!Protein_Seq_names.empty()) { // Print out what we read from the last entry
//std::cout << "\tReseting to new sequence" << std::endl;
// cout << Protein_Sequences << endl;
Protein_Seq_names.clear();
Prot_Seq1 = Protein_Sequences;
}
if (!line.empty()) {
//std::cout << "\tSetting sequence start" << std::endl;
Protein_Seq_names = line.substr(1);
}
// std::cout << "\tClearing sequences..." << std::endl;
Protein_Sequences.clear();
}
else if (!Protein_Seq_names.empty()) {
line = line.substr(0, line.length() - 1);
if (line.find(' ') != string::npos) { // Invalid sequence--no spaces allowed
//std::cout << "\tSpace found, clearing buffers..." << std::endl;
Protein_Seq_names.clear();
Protein_Sequences.clear();
}
else {
//std::cout << "\tAppending line to protein sequence..." << std::endl;
Protein_Sequences += line;
}
}
//std::cout << "Protein_Sequences: " << Protein_Sequences << std::endl;
}
if (!Protein_Seq_names.empty()) { // Print out what we read from the last entry
// cout << Protein_Sequences << endl;
Prot_Seq2 = Protein_Sequences;
}
cout << "\nSequence 1: " << Prot_Seq1 << endl;
cout << Prot_Seq1.length();
cout << "\nSequence 2: " << Prot_Seq2 << endl;
cout << Prot_Seq2.length();
}
Assuming your file doesn't end with a new line then the last call to std::getline will set the eof bit to indicate that it reached the end of the file before finding the line ending. As you are checking .good() in your while loop the last line will be discarded. You should instead check !fail() (or just the boolean value of the stream itself which is equivalent to !fail()):
while (getline(myfile, line))
After reading the final line the next iteration of the loop will try to read whilst the stream is in the eof state and immediately fail and break out of the loop.

Debug Error Thrown When Checking !str.find()

I'm learning C++. I'm developing a simple "library management" application that allows users to create an account, check out books, etc. Each book is managed using a unique text file. The text file contains three lines as follows, however the third line is the only important thing here, as it contains the owner of the book.
The following code prints the contents of an additional text file that contains a list of all the books, but that shouldn't be relevant to the error. It converts the contents of the text file to a string, and then checks to see if "NA" is present. If "NA" is present, it is replaced with the current username. The file is then reopened using ios::trunc to wipe the file, and the new string is passed into the file. This works fine.
The issue is that when running the application, if a username is already there instead of "NA", I get a Debug Error that only reads abort() has been called. I've tried debugging, but I can't get any more information.
This is the error and the code:
void bookCheckout()
{
system("CLS");
string line;
string bookChoice;
ifstream checkBookList;
ofstream checkOutBook;
checkBookList.open("books/booklist.txt");
string sTotal;
string s;
cout << "<---Avaliable Books--->" << endl;
while (getline(checkBookList, line)) {
cout << line << endl;
}
checkBookList.close();
cout << "\nWhat Book Would You Like?:";
cin >> bookChoice;
checkBookList.open("books/" + bookChoice + ".txt");
while (!checkBookList.eof()) {
getline(checkBookList, s);
sTotal += s + "\n";
}
checkBookList.close();
if (sTotal.find("NA")) {
sTotal.replace(sTotal.find("NA"), 2, globalUsername);
checkOutBook.open("books/" + bookChoice + ".txt", ios::trunc);
checkOutBook << sTotal;
checkOutBook.close();
}
else if (!sTotal.find("NA")) {
cout << "Book already checked out!" << endl;
}
checkOutBook.close();
system("PAUSE");
}
There are a few issues with your code:
lack of error handling.
while (!checkBookList.eof()) - see Why is iostream::eof inside a loop condition (i.e. while (!stream.eof())) considered wrong?
if (sTotal.find("NA")) - string::find() returns an INDEX, not a BOOLEAN. If 0 is returned, that will be evaluated as false. All other values will be evaluated as true, including string::npos (-1), which is what string::find() returns if a match is not found.
Also, your goal is to check if the 3RD LINE SPECIFICALLY is "NA", so using string::find() is not the best choice for that purpose. Think of what would happen if the 1st or 2nd line happened to contain the letters NA. Your code logic would not behave properly.
else if (!sTotal.find("NA")) - no need to call find() in the else at all. Just use else by itself.
With that said, try something more like this:
void bookCheckout()
{
system("CLS");
ifstream checkBookList;
checkBookList.open("books/booklist.txt");
if (!checkBookList.is_open()) {
cerr << "Unable to open booklist.txt" << endl;
return;
}
string line;
cout << "<---Available Books--->" << endl;
while (getline(checkBookList, line)) {
cout << line << endl;
}
checkBookList.close();
cout << "\nWhat Book Would You Like?:";
string bookChoice;
getline(cin, bookChoice);
checkBookList.open("books/" + bookChoice + ".txt");
if (!checkBookList.is_open()) {
cerr << "Unable to open " + bookChoice + ".txt for reading" << endl;
return;
}
string sTotal, sOwner;
int lineNum = 0;
string::size_type ownerIndex = string::npos;
while (getline(checkBookList, line)) {
++lineNum;
if (lineNum == 3) {
sOwner = line;
ownerIndex = sTotal.size();
}
sTotal += line + "\n";
}
checkBookList.close();
if (sOwner == "NA") {
sTotal.replace(ownerIndex, 2, globalUsername);
ofstream checkOutBook("books/" + bookChoice + ".txt", ios::trunc);
if (!checkOutBook.is_open()) {
cerr << "Unable to open " + bookChoice + ".txt for writing" << endl;
return;
}
checkOutBook << sTotal;
checkOutBook.close();
cout << "Book checked out!" << endl;
}
else {
cout << "Book already checked out by " << sOwner << "!" << endl;
}
system("PAUSE");
}
Alternatively, use a std::vector to gather the book contents, that will give you indexed access to each line:
#include <vector>
void bookCheckout()
{
system("CLS");
ifstream checkBookList;
checkBookList.open("books/booklist.txt");
if (!checkBookList.is_open()) {
cerr << "Unable to open booklist.txt" << endl;
return;
}
string line;
cout << "<---Available Books--->" << endl;
while (getline(checkBookList, line)) {
cout << line << endl;
}
checkBookList.close();
cout << "\nWhat Book Would You Like?:";
string bookChoice;
getline(cin, bookChoice);
checkBookList.open("books/" + bookChoice + ".txt");
if (!checkBookList.is_open()) {
cerr << "Unable to open " + bookChoice + ".txt for reading" << endl;
return;
}
vector<string> sTotal;
sTotal.reserve(3);
while (getline(checkBookList, line)) {
sTotal.push_back(line);
}
while (sTotal.size() < 3) {
sTotal.push_back("");
}
checkBookList.close();
if (sTotal[2] == "" || sTotal[2] == "NA") {
sTotal[2] = globalUsername;
ofstream checkOutBook("books/" + bookChoice + ".txt", ios::trunc);
if (!checkOutBook.is_open()) {
cerr << "Unable to open " + bookChoice + ".txt for writing" << endl;
return;
}
for(size_t i = 0; i < sTotal.size(); ++i) {
checkOutBook << sTotal[i] << '\n';
}
checkOutBook.close();
cout << "Book checked out!" << endl;
}
else {
cout << "Book already checked out by " << sTotal[2] << "!" << endl;
}
system("PAUSE");
}

FASTA reader written in C++?

Let me start off by stating that I am a beginner in C++. Anyways, the FASTA format goes as follows:
Any line starting with a '>' indicates the name/id of the gene sequence right below it. There is a gene sequence right below the id. This gene sequence can be 1 or multiple lines.
So... what I want to do is print: id << " : " << gene_sequence << endl;
Here is my code:
#include <iostream>
#include <fstream>
int main(int argc, char **argv) {
if (argc < 2) {
std::cerr << " Wrong format: " << argv[0] << " [infile] " << std::endl;
return -1;
}
std::ifstream input(argv[1]);
if (!input.good()) {
std::cerr << "Error opening: " << argv[1] << " . You have failed." << std::endl;
return -1;
}
std::string line, id, DNA_sequence;
while (std::getline(input, line).good()) {
if (line[0] == '>') {
id = line.substr(1);
std::cout << id << " : " << DNA_sequence << std::endl;
DNA_sequence.clear();
}
else if (line[0] != '>'){
DNA_sequence += line;
}
}
}
For the second argument inputted into the command line, here is the content of my file:
>DNA_1
GATTACA
>DNA_2
TAGACCA
TAGACCA
>DNA_3
ATAC
>DNA_4
AT
Please copy and paste into text file.
After this has been done, and the code has been executed, I want to point out the problem. The code skips inputting the sequence of DNA_1 into its correct respective place, and instead placing DNA_1 's sequence into DNA_2. The results get pushed forward 1 as a result. Any assistance or tips would be greatly appreciated?
As I've said before, I am new to C++. And the semantics are quite hard to learn compared to Python.
I see a few problems with your code.
First you loop on std::ifstream::good() which doesn't work because it won't allow for End Of File (which happens even after a good read).
Then you access line[0] without checking if the line is empty which could cause a seg-fault.
Next you output the "previous line" before you have even collected it.
Finally you don't output the final line because the loop terminates when it doesn't find another >.
I added comments to my corrections to your code:
#include <iostream>
#include <fstream>
int main(int argc, char **argv) {
if (argc < 2) {
std::cerr << " Wrong format: " << argv[0] << " [infile] " << std::endl;
return -1;
}
std::ifstream input(argv[1]);
if (!input.good()) {
std::cerr << "Error opening: " << argv[1] << " . You have failed." << std::endl;
return -1;
}
std::string line, id, DNA_sequence;
// Don't loop on good(), it doesn't allow for EOF!!
// while (std::getline(input, line).good()) {
while (std::getline(input, line)) {
// line may be empty so you *must* ignore blank lines
// or you have a crash waiting to happen with line[0]
if(line.empty())
continue;
if (line[0] == '>') {
// output previous line before overwriting id
// but ONLY if id actually contains something
if(!id.empty())
std::cout << id << " : " << DNA_sequence << std::endl;
id = line.substr(1);
DNA_sequence.clear();
}
else {// if (line[0] != '>'){ // not needed because implicit
DNA_sequence += line;
}
}
// output final entry
// but ONLY if id actually contains something
if(!id.empty())
std::cout << id << " : " << DNA_sequence << std::endl;
}
Output:
DNA_1 : GATTACA
DNA_2 : TAGACCATAGACCA
DNA_3 : ATAC
DNA_4 : AT
You're storing the new id before printing the old one:
id = line.substr(1);
std::cout << id << " : " << DNA_sequence << std::endl;
Swap the lines around for proper order. You probably also want to check if you have any id already present to skip the first entry.
working implementation is here
https://rosettacode.org/wiki/FASTA_format#C.2B.2B
only corrected
while( std::getline( input, line ).good() ){
to
while( std::getline( input, line ) ){
Code
#include <iostream>
#include <fstream>
int main( int argc, char **argv ){
if( argc <= 1 ){
std::cerr << "Usage: "<<argv[0]<<" [infile]" << std::endl;
return -1;
}
std::ifstream input(argv[1]);
if(!input.good()){
std::cerr << "Error opening '"<<argv[1]<<"'. Bailing out." << std::endl;
return -1;
}
std::string line, name, content;
while( std::getline( input, line ) ){
if( line.empty() || line[0] == '>' ){ // Identifier marker
if( !name.empty() ){ // Print out what we read from the last entry
std::cout << name << " : " << content << std::endl;
name.clear();
}
if( !line.empty() ){
name = line.substr(1);
}
content.clear();
} else if( !name.empty() ){
if( line.find(' ') != std::string::npos ){ // Invalid sequence--no spaces allowed
name.clear();
content.clear();
} else {
content += line;
}
}
}
if( !name.empty() ){ // Print out what we read from the last entry
std::cout << name << " : " << content << std::endl;
}
return 0;
}
input:
>Rosetta_Example_1
THERECANBENOSPACE
>Rosetta_Example_2
THERECANBESEVERAL
LINESBUTTHEYALLMUST
BECONCATENATED
output:
Rosetta_Example_1 : THERECANBENOSPACE
Rosetta_Example_2 : THERECANBESEVERALLINESBUTTHEYALLMUSTBECONCATENATED

Reading from a file C++ Ifstream

I am trying to do a problem using dynamic programming to find which items to select from a text file under a certain weight capacity while maximizing value. The file is in the format:
item1, weight, value
item2, weight, value
With a variable number of items. I am having a problem reading the file in the main method, where I am trying to error check for bad input. My problem comes from when I check if the weight is either missing, or is a string instead of an int. I can't figure out how to check separately for a string or if there is nothing in that spot. I am supposed to output a different error depending on which it is. Thanks.
int main(int argc, char * const argv[])
{
std::vector<Item> items;
if (argc != 3)
{
std::cerr << "Usage: ./knapsack <capacity> <filename>";
return -1;
}
int capacity;
std::stringstream iss(argv[1]);
if (!(iss >> capacity) || (capacity < 0))
{
std::cerr << "Error: Bad value '" << argv[1] << "' for capacity." << std::endl;
return -1;
}
std::ifstream ifs(argv[2]);
if (ifs.is_open())
{
std::string description;
unsigned int weight;
unsigned int value;
int line = 1;
while (!ifs.eof())
{
if (!(ifs >> description))
{
std::cerr << "Error: Line number " << line << " does not contain 3 fields." << std::endl;
return -1;
}
if (!(ifs >> weight))
{
if (ifs.eof())
std::cerr << "Error: Line number " << line << " does not contain 3 fields." << std::endl;
else
std::cerr << "Error: Invalid weight '" << ifs << "' on line number " << line << "." << std::endl;
return -1;
}
else if (!(ifs >> weight) || (weight < 0))
{
std::cerr << "Error: Invalid weight '" << ifs << "' on line number " << line << "." << std::endl;
return -1;
}
if (!(ifs >> value))
{
if (ifs.eof())
std::cerr << "Error: Line number " << line << " does not contain 3 fields." << std::endl;
else
std::cerr << "Error: Invalid value '" << ifs << "' on line number " << line << "." << std::endl;
return -1;
}
else if (!(ifs >> value) || (value < 0))
{
std::cerr << "Error: Invalid value '" << ifs << "' on line number " << line << "." << std::endl;
return -1;
}
Item item = Item(line, weight, value, description);
items.push_back(item);
line++;
}
ifs.close();
knapsack(capacity, items, items.size());
}
else
{
std::cerr << "Error: Cannot open file '" << argv[2] << "'." << std::endl;
return -1;
}
return 0;
}
I think better approach will be using getline function, by which you will take a whole line and then you can try to split it into three strings. If three parts are not found or any of the three parts is not in a right format you can output an error.
An example code is give below,
#include<fstream>
#include<iostream>
#include<vector>
#include <sstream>
#include<string>
using namespace std;
void split(const std::string &s, std::vector<std::string> &elems, char delim) {
elems.clear();
std::stringstream ss(s);
std::string item;
while (std::getline(ss, item, delim)) {
elems.push_back(item);
}
}
int main()
{
vector<string> elems;
ifstream fi("YOUR\\PATH");
string inp;
while(getline(fi, inp))
{
split(inp, elems, ' ');
if(elems.size() != 3)
{
cout<<"error\n";
continue;
}
int value;
bool isint = istringstream(elems[1])>>value;
if(!isint)
{
cout << "error\n";
continue;
}
cout<<"value was : " << value << endl;
}
return 0;
}
You could read the whole line at once and check it's format using regular expressions.

How to convert a text file from Windows to Unix

When converting from Unix to Windows, I get the correct output; however, when going from Windows to Unix I get some strange output. I thought all I had to allow for was the removal of the carriage return, '\r'. This isn't working though. When I open the text file after running the code, I get some strange results, the first line is correct, and then all hell breaks lose.
int main( )
{
bool windows = false;
char source[256];
char destination[256]; // Allocate the max amount of space for the filenames.
cout << "Please enter the name of the source file: ";
cin >> source;
ifstream fin( source, ios::binary );
if ( !fin ) // Check to make sure the source file exists.
{
cerr << "File " << source << " not found!";
getch();
return 1;
}//endif
cout << "Please enter the name of the destination file: ";
cin >> destination;
ifstream fest( destination );
if ( fest ) // Check to see if the destination file already exists.
{
cout << "The file " << destination << " already exists!" << endl;
cout << "If you would like to truncate the data, please enter 'Y', "
<< "otherwise enter 'N' to quit: ";
char answer = char( getch() );
if ( answer == 'n' || answer == 'N' )
{
return 1;
}//endif
}//endif
clrscr(); // Clear screen for neatness.
ofstream fout( destination, ios::binary );
if ( !fout.good() ) // Check to see if the destination file can be edited.
{
cout << destination << "could not be opened!" << endl;
getch();
return 1;
}//endif
// Open the destination file in binary mode.
fout.open( destination, ios::binary );
char ch = fin.get(); // Set ch to the first char in the source file.
while ( !fin.eof() )
{
if ( ch == '\x0D' ) // If ch is a carriage return, then the source file
{ // must be in a windows format.
windows = true;
}//endif
if ( windows == true )
{
ch = fin.get(); // Advance ch, so that at the bottom of the loop, the
}//endif // carriage return is not coppied into the new file.
if ( windows == false )
{
if ( ch == '\x0A' ) // If the file is in the Unix format..
{
fout.put( '\x0D' ); // When a new line is found, output a carriage
}//endif // return.
}//endif
fout.put( ch );
ch = fin.get();
}//endwh
if ( windows == true )
{
fout.put( '\x0A' );
}//endif
fout.close();
fin.close(); // Close yer files.
if ( windows == true ) // A little output for user-friendly-ness.
{
cout << "The contents of " << source << " have been coppied to "
<< destination << " and converted to Unix format." << endl;
}else{
cout << "The contents of " << source << " have been coppied to "
<< destination << " and converted to Windows format." << endl;
}//endif
cout << "Enter any key to quit.." << endl;
getch();
return 0;
}//endmn
*If* you only need to convert simple ascii (and perhaps utf-8) text files, you could read the source file line-by-line in a loop in translated mode (handles newlines for you enough for this case) with non-member getline() and then output the lines to the output file while inserting \n or \r\n after each line except the last.
Then, you can remove the original file and rename the temp file to have the original file's name. Or, if you want, you can instead push_back the lines into a vector<string>. Then, you could close the input handle to the file, do ofstream out("filename", ios_base::trunc) and write the elements of the vector to the file while separating them by the newlines you want.
It all depends on your requirements.
The following is an example with minimal error handling. But, it's really only the FOR loop and reading line-by-line that I want to show here as a different way of doing things.
convert_file.exe "test.txt" "linux"
convert_file.exe "test.txt" "win"
#include <iostream>
#include <string>
#include <fstream>
#include <ostream>
#include <cstdlib>
#include <cstdio>
using namespace std;
int main(int argc, char* argv[]) {
if (argc != 3) {
cerr << "Usage: this.exe file_to_convert newline_format(\"linux\" or \"win\")" << endl;
return EXIT_FAILURE;
}
string fmt(argv[2]);
if (fmt != "linux" && fmt != "win") {
cerr << "Invalid newline format specified" << endl;
return EXIT_FAILURE;
}
ifstream in(argv[1]);
if (!in) {
cerr << "Error reading test.txt" << endl;
return EXIT_FAILURE;
}
string tmp(argv[1]);
tmp += "converted";
ofstream out(tmp.c_str(), ios_base::binary);
if (!out) {
cerr << "Error writing " << tmp << endl;
return EXIT_FAILURE;
}
bool first = true;
for (string line; getline(in, line); ) {
if (!first) {
if (fmt == "linux") {
out << "\n";
} else {
out << "\r\n";
}
}
out << line;
first = false;
}
in.close();
out.close();
if (remove(argv[1]) != 0) {
cerr << "Error deleting " << argv[1] << endl;
return EXIT_FAILURE;
}
if (rename(tmp.c_str(), argv[1]) != 0) {
cerr << "Error renaming " << tmp << " to " << argv[1] << endl;
return EXIT_FAILURE;
}
}
As others have said though, there are already utilities (including text editors like Notepadd++) that do newline conversion for you. So, you don't need to implement anything yourself unless you're doing this for other reasons (you didn't specify).
Don't worry about checking for windows in the loop. Simply check for a carriage return. Set a variable 'carriage_return.' Next iteration, if 'carriage-return' and ch != linefeed, simply insert a linefeed. Then reset the carriage_return variable to false. It's a very simple and basic rule which won't send you wrong.
bool carriage_return = false;
const char linefeed = '\n'; // Is it? I forget.
const char cr = '\r'; // I forget again. Too late to check.
char ch = fin.get();
if (ch == cr) carriage_return = true;
while (!fin.eof()){
if (carriage_return) { // Check if we already have a newline
if (ch != linefeed) { // If we have a newline previously, we need a linefeed. If it's already there just leave it, if it isn't there put it in
fout.put(linefeed);
}
if (ch != cr) carriage_return = false; // Reset the carriage-return flag *if* we don't have another carriage return. This handles multiple empty lines in an easy way for us.
}
fout.put(ch);
ch = fin.get();
}
I have re-edit your code and it works fine for me..
Hope this helps !
#include <iostream>
#include <fstream>
#include <iostream>
#include<stdio.h>
using namespace std;
int main( )
{
bool windows = false;
char source[256];
char destination[256]; // Allocate the max amount of space for the filenames.
cout << "Please enter the name of the source file: ";
cin >> source;
ifstream fin( source, ios::binary );
if ( !fin ) // Check to make sure the source file exists.
{
cerr << "File " << source << " not found!";
return 1;
}//endif
cout << "Please enter the name of the destination file: ";
cin >> destination;
ifstream fest( destination );
if ( fest ) // Check to see if the destination file already exists.
{
cout << "The file " << destination << " already exists!" << endl;
cout << "If you would like to truncate the data, please enter 'Y', "
<< "otherwise enter 'N' to quit: ";
char answer;
cin >> answer;
if ( answer == 'n' || answer == 'N' )
{
return 1;
}
}
//clrscr();
ofstream fout( destination);
if ( !fout.good() )
{
cout << destination << "could not be opened!" << endl;
return 1;
}
char ch = fin.get();
while (!fin.eof())
{
if ( ch == '\r' )
{
windows = true;
}
if ( ch == '\n' && windows == false ) // If the file is in the Unix format..
{
// Don't do anything here
}
fout.put( ch );
cout << ch; // For Debugging purpose
ch = fin.get();
}
fout.close();
fin.close();
if ( windows == true ) // A little output for user-friendly-ness.
{
cout<<endl;
cout << "The contents of " << source << " have been coppied to "
<< destination << " and converted to Unix format." << endl;
}else{
cout << "The contents of " << source << " have been coppied to "
<< destination << " and converted to Windows format." << endl;
}//endif
cout << "Enter any key to quit.." << endl;
return 0;
}
Did you make sure you're reading in the data in the right format and saving it in the right format?
Trying to work with a different character encoding and just 'reading' it in leads to very bad things :|
And you then also have to account for different replacements that need to be done.
This may help link