Closed. This question needs details or clarity. It is not currently accepting answers.
Want to improve this question? Add details and clarify the problem by editing this post.
Closed 9 years ago.
Improve this question
Could you help me find what is error with this 2 line which I took from the below line. Since I am a newbie in c++, I need your help folks. In addition, how to change this code to c++ because I am used to C programming language rather than C++
fgets( line, 80, in )
error :
rewind( in );
rows = countLines(in);
Code:
int container:: countLines( ifstream in )
{
int count = 0;
char line[80];
if ( in.good())
{
while ( !in.eof() )
if (in>>line ) count++;
rewind( in );
}
return count;
}
// opens the file and stores the strings
//
// input: string of passenger data
// container to store strings
//
int container:: processFile( char* fn )
{
char line[80];
ifstream in ;
in.open(fn);
int count = 0;
if ( !in.fail() )
{
rows = countLines(in);
strings = new char* [rows];
while ( !in.eof() )
{
if ( in>>line )
{
strings[count] =new char [strlen(line)+1];
strcpy(strings[count],line);
count++;
}
}
}
else
{
//printf("Unable to open file %s\n",fn);
//cout<<"Unable to open file "<<fn<<endl;
exit(0);
}
in.close();
return count;
}
Generally, when you pass a stream argument you don't pass by value:
int container:: countLines( ifstream in )
You pass by reference:
int container:: countLines( ifstream& in )
This logic is wrong:
if ( in.good())
{
while ( !in.eof() )
if (in>>line ) count++;
}
Don't use eof() this way. Instead:
while (in >> line)
count++;
This is how to rewind in C:
rewind( in );
In C++, look at the seekg function:
http://en.cppreference.com/w/cpp/io/basic_istream/seekg
Prefer to use std::string over char*:
strings = new char* [rows];
Again, don't use eof():
while (in >> line)
{
strings[count] =new char [strlen(line)+1];
strcpy(strings[count],line);
count++;
}
Related
Closed. This question needs details or clarity. It is not currently accepting answers.
Want to improve this question? Add details and clarify the problem by editing this post.
Closed 7 months ago.
Improve this question
Let’s say I have 128 and I broke it up into 1, 2 and 8.
Can anyone give me a logic to build the number again from its broken digits.
If you have a vector of ints, you can do:
#include <vector>
#include <cstdio>
#include <cstdlib>
int to_integer( const std::vector<int>& v ) {
int number = 0;
for ( int value : v ) {
number = 10*number + value;
}
return number;
}
int main() {
std::vector<int> vec {1,2,8};
int number = to_integer( vec );
printf( "Number:%d\n", number );
return 0;
}
If it was a string, you could simply use the C library
#include <cstdlib>
...
const char* str = "128";
int number = ::atoi( str );
But that's likely not what you are asking.
You can do the hands-on approach
#include <cstdio>
int to_integer( const char* str ) {
int number = 0;
for ( ; *str != '\0'; str++ ) {
char ch = *str;
number = 10*number + (ch-'0');
}
return number;
}
int main() {
const char* str = "128";
int number = to_integer( str );
printf( "Number:%d\n", number );
return 0;
}
Please note that this routine above is just a minimal, simplistic and it does not check for cases that the C library does as eg: non-numeric charaters, white spaces, null pointer.
However many times we can guarantee all above as a precondition so the above becomes valid production code. I actually use something like that for high speed trading.
I am wondering how to read a specific value from a csv file in C++, and then read the next four items in the file. For example, this is what the file would look like:
fire,2.11,2,445,7891.22,water,234,332.11,355,5654.44,air,4535,122,334.222,16,earth,453,46,77.3,454
What I want to do is let my user select one of the values, let's say "air" and also read the next four items(4535 122 334.222 16).
I only want to use fstream,iostream,iomanip libraries. I am a newbie, and I am horrible at writing code, so please, be gentle.
You should read about parsers. Full CSV specifications.
If your fields are free of commas and double quotes, and you need a quick solution, search for getline/strtok, or try this (not compiled/tested):
typedef std::vector< std::string > svector;
bool get_line( std::istream& is, svector& d, const char sep = ',' )
{
d.clear();
if ( ! is )
return false;
char c;
std::string s;
while ( is.get(c) && c != '\n' )
{
if ( c == sep )
{
d.push_back( s );
s.clear();
}
else
{
s += c;
}
}
if ( ! s.empty() )
d.push_back( s );
return ! s.empty();
}
int main()
{
std::ifstream is( "test.txt" );
if ( ! is )
return -1;
svector line;
while ( get_line( is, line ) )
{
//...
}
return 0;
}
Closed. This question needs debugging details. It is not currently accepting answers.
Edit the question to include desired behavior, a specific problem or error, and the shortest code necessary to reproduce the problem. This will help others answer the question.
Closed 8 years ago.
Improve this question
I am trying to create my own char copy (like strcopy) function. It needs to be custom, I cannot use the one inside the std lib (Strcopy). I cannot change my char arrays to strings. I cannot use built in functions to copy the chars. i must use pointers to manipulate the array
I am trying to work off what the real strcopy looks like, but i cant seem toget mine to work. I THINK I want to create a pointer that points to strcpy(char destination,char source) and then I manipulate destination through source but nothing is copying over correctly and I just print out garbage. Please help open to all suggestions
void StrCpy(char *destination, char *source);
int main()
{
card card1[10], card2[10];
card1[0].cvalue = 1000;
card2[0].cvalue = 90000;
card *card1p = &card1[0];
card *card2p = &card2[0];
//set up card file to be read in
ifstream fin;
string finName;
//get file name from user
cout << "Enter file name...(cardFile.txt)" << endl;;
getline(cin,finName);
//open the file
fin.open(finName.c_str());
//check if cardFile.txt opens correctly
if(!fin.good())
{
cout << "Error with card file" << endl;
return 0;
}
else
{
card *deckPointer = NULL;
//prime fin
//fin >> deck[i].suit;
//fin >> deck[i].rank;
//fin >> deck[i].cvalue;
while(fin.good())
{
for(card1p = &card1[0]; card1p < &card1[10];card1p++)
{
fin >> (*card1p).suit;
fin >> (*card1p).rank;
fin >> (*card1p).cvalue;
}
}
}
StrCpy((*card2p).suit, (*card1p).suit);
cout << (*card2).suit << endl;
}
void StrCpy(char *destination,char *source)
{
char *p = destination;
*p = *source;
}
Usually the function is defined the following way
char * StrCpy( char *destination, const char *source )
{
char *p = destination;
while ( *p++ = *source++ );
return destination;
}
Yet another realization
char * StrCpy( char *destination, const char *source )
{
char *p = destination;
do
{
*p++ = *source;
} while ( *source++ );
return destination;
}
And one more
char * StrCpy( char *destination, const char *source )
{
for ( char *p = destination; *p = *source; ++p, ++source );
return destination;
}
or
char * StrCpy( char *destination, const char *source )
{
char *p = destination;
while ( *p = *source ) ++p, ++source;
return destination;
}
void StrCpy(char *destination,char *source)
{
char *p = destination;
*p = *source;
}
is not correct. Basically you're doing
destination[0] = source[0], which is not a actual copy.
You should read up on pointers. And formatting. And std::string.
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.
Closed. This question is off-topic. It is not currently accepting answers.
Want to improve this question? Update the question so it's on-topic for Stack Overflow.
Closed 11 years ago.
Improve this question
There is my source code loading text file and delimitting each line to single items (words).
How to further optimize the code? Testing empty lines (and other constructions) are (in my opinion) a little bit inefficient....
typedef std::vector < std::string > TLines;
typedef std::vector < std::vector < std::string > > TItems;
TItems TFloadFile ( const char * file_name )
{
//Load projection from file
unsigned int lines = 0;
char buffer[BUFF];
FILE * file;
TItems file_words;
TLines file_lines;
file = fopen ( file_name, "r" );
if ( file != NULL )
{
for ( ; fgets ( buffer, BUFF, file ); )
{
//Remove empty lines
bool empty_line = true;
for ( unsigned i = 0; i < strlen ( buffer ); i++ )
{
if ( !isspace ( ( unsigned char ) buffer[i] ) )
{
empty_line = false;
break;
}
}
if ( !empty_line )
{
file_lines.push_back ( buffer );
lines++;
}
}
file_words.resize ( lines + 1 );
for ( unsigned int i = 0; i < lines; i++ )
{
char * word = strtok ( const_cast<char *> ( file_lines[i].c_str() ), " \t,;\r\n" );
for ( int j = 0; word; j++, word = strtok ( 0, " \t;\r\n" ) )
{
file_words[i].push_back ( word );
}
}
fclose ( file );
}
return file_words;
}
Thanks for your help...
The line for ( unsigned i = 0; i < strlen ( buffer ); i++ ) is quite inefficient as you're calculating the length of buffer each time through the loop. However, it's possible that this will be optimised away by the compiler.
You're pushing items onto your std::vectors without reserve()ing any space. For large file, this will involve a lot of overhead as the content of the vectors will need to be copied in order to resize them. I just read #Notinlist's answer, which already talks about the inefficiencies of std::vector::resize().
Instead of reading each line into a vector through repeated fgets() calls, could you not simply determine the number of bytes in the file, dynamically allocate a char array to hold them, and then dump the bytes into it? Then, you could parse the words and store them in file_words. This would be more efficient than the method you're currently using.
Before optimizing, can you explain how big the file is, how long the code currently takes to execute and why you think it isn't already IO bound (ie due to hard disk speed). How long do you think it should take? Some idea of the type of data in the file would be good too (such as average line length, average proportion of empty lines etc).
That said, combine the remove-empty-line loop with the word-tokenising loop. Then you can remove TLines altogether and avoid the std::string constructions and vector push-back. I haven't checked this code works, but it should be close enough to give you the idea. It also includes a more efficient empty line spotter:
if ( file != NULL )
{
for ( ; fgets ( buffer, BUFF, file ); )
{
bool is_empty = true;
for (char *c = buffer; *c != '\0'; c++)
{
if (!isspace(c))
{
is_empty = false;
break;
}
}
if (is_empty)
continue;
file_words.resize ( lines + 1 );
char * word = strtok ( buffer, " \t,;\r\n" );
for ( int j = 0; word; j++, word = strtok ( 0, " \t;\r\n" ) )
{
file_words[i].push_back ( word );
}
lines++;
}
fclose ( file );
}
For one
file_lines.push_back ( buffer );
That is a very expensive line. If you don't have to use vector, then use a list instead. Maybe convert your list to a vector after you finished with the job.
If you absolutely in need of using vector for this purpose, then you should use some exponential increment instead, like:
if(file_lines.size()<=lines){
file_lines.resize((int)(lines * 1.3 + 1));
}
That way you will have much less cpu intensive .resize() operations, for a cost of minimal memory consumption overhead.
Simplified and converted to use std::list instead of std::vector.
typedef std::list< std::list< std::string > > TItems;
TItems TFloadFile ( const char * file_name )
{
using namespace std;
//Load projection from file
ifstream file(file_name);
TItems file_words;
string line;
for(getline(file,line); !file.fail() && !file.eof(); getline(file,line))
{
file_words.push_back(list<string>());
list<string> &words(file_words.back());
char *word = strtok((char*)line.c_str(), " \t,;\r\n" );
for(; word; word=strtok( 0, " \t;\r\n" ))
{
words.push_back( word );
}
if(!words.size())
file_words.pop_back();
}
return file_words;
}