This question already has answers here:
Edit a specific row in a file
(5 answers)
Closed 6 years ago.
I have a txt file like this:
"shoes":12
"pants":33
"jacket":26
"glasses":16
"t-shirt":182
I need to replace the number of jacket ( from 26 to 42 for example ). So, I have wrote this code, but I don't know how to edit a specific row where there is the word "jacket":
#include <iostream>
#include <fstream>
using namespace std;
int main() {
ifstream f("file.txt");
string s;
if(!f) {
cout< <"file does not exist!";
return -1;
}
while(f.good())
{
getline(f, s);
// if there is the "jacket" in this row, then replace 26 with 42.
}
f.close();
return 0;
}
In order to modify data in a text file, you'll generally have to read
the entire file into memory, make the modifications there, then rewrite
it. In this case, I'd suggest defining a structure for the entries,
with name and quantity entries, equality defined as equality of the
names, and an overloaded operator>> and operator<< to read and write
it from the file. You're overall logic would then use functions like:
void
readData( std::string const& filename, std::vector<Entry>& dest )
{
std::ifstream in( filename.c_str() );
if ( !in.is_open() ) {
// Error handling...
}
dest.insert( dest.end(),
std::istream_iterator<Entry>( in ),
std::istream_iterator<Entry>() );
}
void
writeData( std::string const& filename, std::vector<Entry> const& data )
{
std::ifstream out( (filename + ".bak").c_str() );
if ( !out.is_open() ) {
// Error handling...
}
std::copy( data.begin(), data.end(), std::ostream_iterator<Entry>( out ) );
out.close();
if (! out ) {
// Error handling...
}
unlink( filename.c_str() );
rename( (filename + ".bak").c_str(), filename.c_str() );
}
(I'd suggest raising exceptions in the error handling, so that you don't
have to worry about the else branches of the ifs. Except for the
creation of the ifstream in the first, the error conditions are exceptional.)
First of all, this is not possible in the naive way. Let's say you want to edit said row but write a larger number, there won't be any space in the file. So usually eidts in the middle are done by rewriting the file or writing a copy. Programs may use memory, temp files, etc and hide this from a user, but chaning some bytes in the middle of a file will only work in very restircted environments.
So what you'll want to do is write another file.
...
string line;
string repl = "jacket";
int newNumber = 42;
getline(f, line)
if (line.find(repl) != string::npos)
{
osstringstream os;
os << repl << ':' << newNumber;
line = os.str();
}
// write line to the new file. For exmaple by using an fstream.
...
If the file has to be the same, you can read all lines to memory, if there is enough memory, or use a temp file for either input or output.
Related
I'm trying to read a file of int's and double's into a vector but I am having difficulty doing so. Given something like:
1 2.1 3 4
2 4
3
9 0.1
How can I use ifstream and the getline function to convert the string into integers and doubles & inserting this into a vector?
I know this is incorrect but I am thinking of something along the lines of:
vector<Pair *> vec; //Pair is a class that contains a int & a double data member
string str;
double num;
ifstream f;
f.open("name of file");
while(getline(f, str){
num = stod(str);
}
To insert into the vector I believe I can do something along the lines of:
Pair * pairObj = new Pair(x,y); //"x" being of type int and "y" being of type double
v.push_back(pair);
I'm sorry if this is unclear, please let me know and I will do my best to explain myself.
You should just use stream iterators!
#include <iostream> // for IO
#include <vector> // for vector!
#include <iterator> // for stream iterator
#include <algorithm> // for copy (optional)
if you are directly initializing
vector<double>vdata{istream_iterator<double>(ifile),
istream_iterator<double>()};
else use copy or copy_n if you only want a fixed amount of data
copy(istream_iterator<double>(ifile),
istream_iterator<double(),
back_inserter(vdata));
if you are working with a large file i would recommend using this method
vector<doube>vdata;
// this will save alot of time, if you don't resize the vector must keep reallocating data
vdata.reserve(file_size);
copy(istream_iterator<double>(ifile),
istream_iterator<double>(),
back_inserter(vdata));
strtod() is C. Proper C++ uses the >> operator.
Once you have read each line of text, construct a std::istringstream from the string, then use operator>> to parse it.
Something along these line::
std::ifstream f("name of file");
// Check if the file was succesfully opened, etc...
std::string str;
while( getline(f, str))
{
std::istringstream i(str);
std::vector<double> v;
double d;
while (i >> d)
{
v.push_back(d);
}
if (!i.eof())
{
// Must be a parsing failure, deal with it in some way.
}
else
{
// Otherwise, v is the vector of numbers on this line.
}
}
string str;
std::vector< double> vd;
// loop reading lines of input
while( getline( f, str )
{
std::stringstream sst(str);
std::string a;
// loop reading space separated values in line
while( getline( sst, a, ' ' ) )
// conver to double and add to end of vectior
vd.push_back( stod( a );
}
// check for complete pairs
if( vd.size() % 2 )
cout << "Error!"
// loop over pairs
vector< pair<int,double> > vpairs;
for( int kp = 0; kp < vd.size()/2; kp++ )
vpairs.push_back( pair<int,double>( (int)vd[kp*2],vd[kp*2+1) );
I've a task to copy elements from .txt file[direct access file] to .bin file[fixed length record file] (homework).
.txt file holds strings. Every line has one word.
I came up with code below, but I'm not sure if that's what is needed and even slighly correct. Any help will be useful! (I'm new to C++)
#include <iostream>
#include <fstream>
#include <string>
using namespace std;
const int buffer_size = 30;
class Word{
char name[buffer_size];
public:
void setName () // Trying to get every word from a line
{
string STRING;
ifstream infile;
infile.open ("text.txt");
while(!infile.eof()) // To get you all the lines.
{
getline(infile,STRING); // Saves the line in STRING.
}
infile.close();
}
};
void write_record()
{
ofstream outFile;
outFile.open("binFILE.bin", ios::binary | ios::app);
Word obj;
obj.setName();
outFile.write((char*)&obj, sizeof(obj));
outFile.close();
}
int main()
{
write_record();
return 0;
}
NEW APPROACH:
class Word
{
char name[buffer_size];
public:
Word(string = "");
void setName ( string );
string getName() const;
};
void readWriteToFile(){
// Read .txt file content and write into .bin file
string temp;
Word object;
ofstream outFile("out.dat", ios::binary);
fstream fin ("text.txt", ios::in);
getline(fin, temp);
while(fin)
{
object.setName(temp);
outFile.write( reinterpret_cast< const char* >( &object ),sizeof(Word) );
getline(fin, temp);
}
fin.close();
outFile.close();
}
int main()
{
readWriteToFile();
return 0;
}
Word::Word(string nameValue)
{
setName(nameValue);
}
void Word::setName( string nameString )
{
// Max 30 char copy
const char *nameValue = nameString.data();
int len = strlen(nameValue);
len = ( len < 31 ? len : 30);
strncpy(name, nameValue, len);
name[len] = '\0';
}
string Word::getName() const
{
return name;
}
Quick commentary and walk through
#include <iostream>
#include <fstream>
#include <string>
using namespace std;
Avoid using namespace std; while you are learning. It can lead to some really nasty, hard to pin-down bugs as your functions may be silently replaced by functions with the same name in the standard library.
const int buffer_size = 30;
class Word
{
char name[buffer_size];
Since it looks like you are allowed to use std::string why not use it here?
public:
void setName() // Trying to get every word from a line
Really bad name for a function that apparently is supposed to // Trying to get every word from a line
{
string STRING;
ifstream infile;
infile.open("text.txt");
while (!infile.eof()) // To get you all the lines.
{
getline(infile, STRING); // Saves the line in STRING.
}
Few things wrong here. One is the epic Why is iostream::eof inside a loop condition considered wrong?
Next is while the code reads each line, it doesn't do anything with the line. STRING is never stored anywhere.
Finally in a class that sounds as though it should contain and manage a single word, it reads all the words in the file. There may be a case for turning this function into a static factory that churns out a std::vector of Words.
infile.close();
}
};
void write_record()
{
ofstream outFile;
outFile.open("binFILE.bin", ios::binary | ios::app);
ios::app will add onto an existing file. This doesn't sound like what was described in the assignment description.
Word obj;
obj.setName();
We've already coverred the failings of the Word class.
outFile.write((char*) &obj, sizeof(obj));
Squirting an object into a stream without defining a data protocol or using any serialization is dangerous. It makes the file non-portable. You will find that some classes, vector and string prominent among these, do not contain their data. Writing a string to a file may get you nothing more than a count and an address that is almost certainly not valid when the file is loaded.
In this case all the object contains is an array of characters and that should write to file cleanly, but it will always write exactly 30 bytes and that may not be what you want.
outFile.close();
}
int main()
{
write_record();
return 0;
}
Since this is homework I'm not writing this sucker for you, but here are a few suggestions:
Read file line by line will get you started on the file reader. Your case is simpler because there is only one word on each line. Your teacher may throw a curveball and add more stuff onto a line, so you may want to test for that.
Read the words from the file into a std::vector. vector will make your job so easy that you might have time for other homework.
A very simplistic implementation is:
std::vector<std::string> words;
while (getline(infile, STRING)) // To get you all the lines.
{
words.push_back(STRING);
}
For writing the file back out in binary, I suggest going Pascal style. First write the length of the string in binary. Use a known, fixed width unsigned integer (no such thing as a negative string) and watch out for endian. Once the length is written, write only the number of characters you need to write.
Ignoring endian, you should have something like this:
uint32_t length = word.length(); // length will always be 32 bits
out.write((char*)&length, sizeof(length));
out.write(word.c_str(), length);
When you are done writing the writer, write a reader function so that you can test that the writer works correctly. Always test your code, and I recommend not writing anything until you know how you'll test it. Very often coming at a program from the test side first will find problems before they even have a chance to start.
The text file provided has an undetermined number of lines, each line containing 3 doubles separated by commas. For example:
-0.30895,0.35076,-0.88403
-0.38774,0.36936,-0.84453
-0.44076,0.34096,-0.83035
...
I want to read this data from the file line by line and then split it on the comma(,) sign and save it in an N by 3 array, let's call it Vertices [N] [3], where N designates the undefined number of lines in the file.
My code so far:
void display() {
string line;
ifstream myfile ("File.txt");
if (myfile.is_open())
{
while ( getline (myfile,line) )
{
// I think the I should do 2 for loops here to fill the array as expected
}
myfile.close();
}
else cout << "Unable to open file";
}
The Problem: I managed to open the file and read line by line, but I have no idea how to pass the values into the requested array.
Thank you.
EDIT:
I have tried modifying my code according to the suggestions i received to the following:
void display() {
string line;
ifstream classFile ("File.txt");
vector<string> classData;
if (classFile.is_open())
{
std::string line;
while(std::getline(classFile, line)) {
std::istringstream s(line);
std::string field;
while (getline(s, field,',')) {
classData.push_back(line);
}
}
classFile.close();
}
else cout << "Unable to open file";
}
Is this the correct? and how can i access each field of the vector i created? (like in an array for example)?
I also noticed that these are of type string, how can i convert them to type float?
Thank you (:
There are many ways to approach this problem. Personally, I would implement a linked-list to save each line read out of the file in its own memory buffer. Once the entire file was read out, I would know how many lines were in the file and process each line in the list using strtok and strtod to convert the values.
Here's some pseudo code to get you rolling:
// Read the lines from the file and store them in a list
while ( getline (myfile,line) )
{
ListObj.Add( line );
}
// Allocate memory for your two-dimensional array
float **Vertices = (float **)malloc( ListObj.Count() * 3 * sizeof(float) );
// Read each line from the list, convert its values to floats
// and store the values in your array
int i = j = 0;
while ( line = ListObj.Remove() )
{
sVal = strtok( line, ",\r\n" );
fVal = (float)strtod( sVal, &pStop );
Verticies[i][j++] = fVal;
sVal = strtok( sVal + strlen(sVal) + 1, ",\r\n" );
fVal = (float)strtod( sVal, &pStop );
Verticies[i][j++] = fVal;
sVal = strtok( sVal + strlen(sVal) + 1, ",\r\n" );
fVal = (float)strtod( sVal, &pStop );
Verticies[i][j] = fVal;
i++;
j = 0;
}
The code after edit is right.You can access a vector value in c++ just like you access the values in a normal c++ array.Like classdata[i]You can find more here . Vector reference
And as for your question about converting string to float.In c++ you can directly do this by using stof ie stof(-0.883) you can find reference again here string to float
Best of luck and hope this helps :)
I am trying to use std::getline() in my project to read in a text file into an array of strings.
Here is my code:
ifstream ifs ( path );
string * in_file;
int count = 0;
while ( !ifs.eof() )
{
++count;
if ( count == 1 )
{
in_file = new string[1];
}
else
{
// Dynamically allocate another space in the stack
string *old_in_file = in_file;
in_file = new string[count];
// Copy over values
for ( int i = 0 ; i < ( count - 1 ) ; i++ )
{
in_file[i] = old_in_file[i];
}
delete[] old_in_file;
}
// After doing some debugging I know this is the problem what am I
// doing wrong with it?
getline(ifs,in_file[count - 1]);
}
So after doing some decoding I know that the getline() is not placing any value in the array of strings. It seems to place a null string in the array.
The goal is to read in a text file like:
Hello
Bye
See you later
The array will be filled like:
in_file [0] = Hello
in_file [1] = Bye
in_file [2] = See you later
Never wrap reading from the stream with the following loop:
while ( !ifs.eof() )
At some websites, you will find an example telling you to do:
while ( ifs.good() )
which is a bit better than the first loop, yet still it is quite error prone and not advisable to do. Have a look at: Why is iostream::eof inside a loop condition considered wrong?
The most common ways of reading the files are either using std::getline when reading by lines:
std::string line;
while ( std::getline(ifs, line) ) {
if (line.empty()) // be careful: an empty line might be read
continue;
...
}
or simply using >> operator when reading by words or extracting concrete types (e.g. numbers):
std::string word;
while ( ifs >> word ) {
...
}
And to your dynamically allocated C-style array of std::string objects: avoid dynamic allocation as much as possible. Believe me, you don't want to take care of memory management on your own. Prefer using objects with automatic storage duration. Take advantage of what the standard library provides.
As it was pointed out already: use STL containers such as std::vector instead of C-style arrays:
std::ifstream ifs(path);
std::vector<std::string> lines;
std::string line;
while ( std::getline(ifs, line) )
{
// skip empty lines:
if (line.empty())
continue;
lines.push_back(line);
}
Why so trouble ?
Simply use std:vector of std::string
std::string str;
std::vector <std::string> vec;
while ( std::getline(ifs,str) )
{
vec.push_back(str) ;
}
If you really need an array of string
do :
string * in_file = new string[vec.size()];
And copy the elements from vec to in_file
for(size_t i=0;i<vec.size();i++)
in_file[i] = vec[i];
I want to read line by line from a file in C or C++, and I know how to do that when I assume some fixed size of a line, but is there a simple way to somehow calculate or get the exact size needed for a line or all lines in file? (Reading word by word until newline is also good for me if anyone can do it that way.)
If you use a streamed reader, all this will be hidden from you. See getline. The example below is based from the code here.
// getline with strings
#include <iostream>
#include <fstream>
#include <string>
using namespace std;
int main () {
string str;
ifstream ifs("data.txt");
getline (ifs,str);
cout << "first line of the file is " << str << ".\n";
}
In C, if you have POSIX 2008 libraries (more recent versions of Linux, for example), you can use the POSIX getline() function. If you don't have the function in your libraries, you can implement it easily enough, which is probably better than inventing your own interface to do the job.
In C++, you can use std::getline().
Even though the two functions have the same basic name, the calling conventions and semantics are quite different (because the languages C and C++ are quite different) - except that they both read a line of data from a file stream, of course.
There isn't an easy way to tell how big the longest line in a file is - except by reading the whole file to find out, which is kind of wasteful.
I would use an IFStream and use getline to read from a file.
http://www.cplusplus.com/doc/tutorial/files/
int main () {
string line;
ifstream myfile ("example.txt");
if (myfile.is_open())
{
while ( myfile.good() )
{
getline (myfile,line);
cout << line << endl;
}
myfile.close();
}
else cout << "Unable to open file";
return 0;
}
You can't get the length of line until after you read it in. You can, however, read into a buffer repeatedly until you reach the end of line.
For programming in c, try using fgets to read in a line of code. It will read n characters or stop if it encounters a newline. You can read in a small buffer of size n until the last character in the string is the newline.
See the link above for more information.
Here is an example on how to read an display a full line of file using a small buffer:
#include <stdio.h>
#include <string.h>
int main()
{
FILE * pFile;
const int n = 5;
char mystring [n];
int lineLength = 0;
pFile = fopen ("myfile.txt" , "r");
if (pFile == NULL)
{
perror ("Error opening file");
}
else
{
do
{
fgets (mystring , n , pFile);
puts (mystring);
lineLength += strlen(mystring);
} while(mystring[strlen ( mystring)-1] != '\n' && !feof(pFile));
fclose (pFile);
}
printf("Line Length: %d\n", lineLength);
return 0;
}
In C++ you can use the std::getline function, which takes a stream and reads up to the first '\n' character. In C, I would just use fgets and keep reallocating a buffer until the last character is the '\n', then we know we have read the entire line.
C++:
std::ifstream file("myfile.txt");
std::string line;
std::getline(file, line);
std::cout << line;
C:
// I didn't test this code I just made it off the top of my head.
FILE* file = fopen("myfile.txt", "r");
size_t cap = 256;
size_t len = 0;
char* line = malloc(cap);
for (;;) {
fgets(&line[len], cap - len, file);
len = strlen(line);
if (line[len-1] != '\n' && !feof(file)) {
cap <<= 1;
line = realloc(line, cap);
} else {
break;
}
}
printf("%s", line);
getline is only POSIX, here is an ANSI (NO max-line-size info needed!):
const char* getline(FILE *f,char **r)
{
char t[100];
if( feof(f) )
return 0;
**r=0;
while( fgets(t,100,f) )
{
char *p=strchr(t,'\n');
if( p )
{
*p=0;
if( (p=strchr(t,'\r')) ) *p=0;
*r=realloc(*r,strlen(*r)+1+strlen(t));
strcat(*r,t);
return *r;
}
else
{
if( (p=strchr(t,'\r')) ) *p=0;
*r=realloc(*r,strlen(*r)+1+strlen(t));
strcat(*r,t);
}
}
return feof(f)?(**r?*r:0):*r;
}
and now it's easy and short in your main:
char *line,*buffer = malloc(100);
FILE *f=fopen("yourfile.txt","rb");
if( !f ) return;
setvbuf(f,0,_IOLBF,4096);
while( (line=getline(f,&buffer)) )
puts(line);
fclose(f);
free(buffer);
it works on windows for Windows AND Unix-textfiles,
it works on Unix for Unix AND Windows-textfiles
Here is a C++ way of reading the lines, using std algorithms and iterators:
#include <iostream>
#include <iterator>
#include <vector>
#include <algorithm>
struct getline :
public std::iterator<std::input_iterator_tag, std::string>
{
std::istream* in;
std::string line;
getline(std::istream& in) : in(&in) {
++*this;
}
getline() : in(0) {
}
getline& operator++() {
if(in && !std::getline(*in, line)) in = 0;
}
std::string operator*() const {
return line;
}
bool operator!=(const getline& rhs) const {
return !in != !rhs.in;
}
};
int main() {
std::vector<std::string> v;
std::copy(getline(std::cin), getline(), std::back_inserter(v));
}