I am trying to read a binary file and store it into a database, however I get a segmentation fault when I try to store a string type into the database. To be exact, the error occurs inside the push function:
new_node->name = name;
I can't seem to find a good solution on the web, and I'm aimlessly trying different things... any help would be appreciated.
//
// loadbin.cpp
//
#include <iostream>
#include <fstream>
#include <string>
#include <vector>
using namespace std;
#include "studentsDB.h"
int main( int argc, char* argv[] ) {
string name;
string id;
int numCourses;
int crn;
vector<int> crns;
studentsDB sDB;
studentsDB::node *students = 0;
int in = 1;
if( argc > 1 ) {
ifstream infile(argv[in], ios::binary );
while( !infile.eof() ) {
infile.read( ( char* )(name.c_str()), sizeof( string ) );
infile.read( ( char* )(id.c_str()), sizeof( string ) );
infile.read( ( char* ) &numCourses, sizeof( int ) );
do{
crns.push_back( crn );
}
while( infile.read( ( char* ) &crn, sizeof( int ) ) );
sDB.push( &students, (string)name, (string)id, numCourses, crns );
}
//sDB.printList( students );
}
else
cout << "Not enough argument" << endl;
}
void studentsDB::push( struct node** head_ref, string name, string id,
int numCourses, vector<int>crns ) {
struct node* new_node = ( struct node* ) malloc(sizeof(struct node));
new_node->name = name;
//new_node->id = id;
new_node->numCourses = numCourses;
//new_node->crns = crns;
new_node->next = (*head_ref);
(*head_ref) = new_node;
size++;
}
This code is bad:
infile.read( ( char* )(name.c_str()), sizeof( string ) );
You can't write to the buffer returned by c_str(), it is not guaranteed to be long enough to hold your result. sizeof(string) has nothing to do with the size the string can hold, by the way. You will need to allocate your own char[] buffer to hold the results of infile.read, then convert to a string afterwards.
Related
I have a file with 9 words and i have to store each word into the char array of 9 pointers but i keep getting an error message. I cannot use vectors!
#include <iostream>
#include <fstream>
using namespace std;
int main()
{
char *words[9];
ifstream inStream;
inStream.open("sentence.txt");
if (inStream.fail())
{
cout << "Input file opening failed.\n";
exit(1);
}
for ( int i = 0; i < 10; i++)
{
inStream >> words[i];
}
inStream.close();
return 0;
}
The declaration
char *words[9];
declares a raw array of pointers. This array is not initialized so the pointers have indeterminate values. Using any of them would be Undefined Behavior.
Instead you want
vector<string> words;
where vector is std::vector from the <vector> header, and string is std::string from the <string> header.
Use the push_back member function to add strings to the end of the vector.
Also you need to move the close call out of the loop. Otherwise it will close the file in the first iteration.
This approach gives the code (off the cuff, disclaimer...)
#include <fstream>
#include <iostream>
#include <vector>
#include <string>
using namespace std;
int main()
{
vector<string> words;
ifstream inStream;
inStream.open("sentence.txt");
for ( int i = 0; i < 10; i++)
{
string word;
if( inStream >> word )
words.push_back( word );
}
inStream.close();
}
If you can't use std::string and std::vector then you need to initialize the array of pointers, and make sure that you don't read more into the buffers than there's room for.
The main problem here is that >> is unsafe for reading into a raw array given by a pointer. It doesn't know how large that array is. It can easily lead to a buffer overrun, with dire consequences.
And so this gets a bit complicated, but it can look like this:
#include <ctype.h> // isspace
#include <fstream>
#include <iostream>
#include <locale.h> // setlocale, LC_ALL
#include <stdlib.h> // EXIT_FAILURE
using namespace std;
void fail( char const* const message )
{
cerr << "! " << message << "\n";
exit( EXIT_FAILURE );
}
void readWordFrom( istream& stream, char* const p_buffer, int const buffer_size )
{
int charCode;
// Skip whitespace:
while( (charCode = stream.get()) != EOF and isspace( charCode ) ) {}
int n_read = 0;
char* p = p_buffer;
while( n_read < buffer_size - 1 and charCode != EOF and not isspace( charCode ) )
{
*p = charCode; ++p;
++n_read;
charCode = stream.get();
}
*p = '\0'; // Terminating null-byte.
if( charCode != EOF )
{
stream.putback( charCode );
if( not isspace( charCode ) )
{
assert( n_read == buffer_size - 1 ); // We exceeded buffer size.
stream.setstate( ios::failbit );
}
}
}
int main()
{
static int const n_words = 9;
static int const max_word_length = 80;
static int const buffer_size = max_word_length + 1; // For end byte.
char *words[n_words];
for( auto& p_word : words ) { p_word = new char[buffer_size]; }
ifstream inStream{ "sentence.txt" };
if( inStream.fail() ) { fail( "Input file opening failed." ); }
setlocale( LC_ALL, "" ); // Pedantically necessary for `isspace`.
for( auto const p_word : words )
{
readWordFrom( inStream, p_word, buffer_size );
if( inStream.fail() ) { fail( "Reading a word failed." ); }
}
for( auto const p_word : words ) { cout << p_word << "\n"; }
for( auto const p_word : words ) { delete[] p_word; }
}
You never allocate any memory for your char* pointers kept in the array.
The idiomatic way to write a c++ code would be:
#include <iostream>
#include <fstream>
#include <vector>
int main() {
std::vector<std::string> words(9);
std::ifstream inStream;
inStream.open("sentence.txt");
for ( int i = 0; inStream && i < 9; i++) {
inStream >> words[i];
}
}
The inStream.close() isn't necessary, and even wrong inside the loop. The std::istream will be closed automatically as soon the variable goes out of scope.
There are a few problems with your code.
char *words[9];
This allocates space for 9 pointers, not nine strings. Since you don't know how big the strings are you have two choices. You can either "guess" how much you'll need and limit the inputs accordingly, or you can use dynamic memory allocation (malloc or new) to create the space you need to store the strings. Dynamic memory would be my choice.
for ( int i = 0; i < 10; i++)
This loop will execute on words[0] through words[9]. However, there is no words[9] (that would be the tenth word) so you'll overwrite memory that you have not allocated
inStream >> words[i];
This will send your input stream to memory that you don't "own". You need to allocate space for the words to live before capturing them from the input stream. To do this correctly, you'll need to know how much space each word will need so you can allocate it.
you could try something like this:
int main()
{
char *words[9];
char tempInput[256]; // space to capture the input, up to a maximum size of 256 chars
ifstream inStream;
inStream.open("sentence.txt");
if (inStream.fail())
{
cout << "Input file opening failed.\n";
exit(1);
}
for ( int i = 0; i < 9; i++)
{
//Clear the input buffer
memset(tempInput, 0, 256);
//Capture the next word
inStream >> tempInput;
//allocate space to save the word
words[i] = new char(strlen(tempInput));
//Copy the word to its final location
strcpy(words[i], tempInput)
}
inStream.close();
return 0;
}
Suppose there is a array..and the contents of the array="ironman" Now, i need to add some additional character to this string like "i*r%o#n#m^a!n"
out[i]="ironman"
Outputs:
out[]=i
out[]=*
out[]=r
out[]=%
out[]=o
out[]=#
out[]=n
out[]=#
out[]=m
out[]=^
out[]=a
out[]=!
out[]=n
I have written a code which concatenate at the end of the string, but i want to concatenate between the string.
char in[20] = "ironman";
const unsigned int symbol_size = 5;
std::string symbols[symbol_size];
std::string out(in);
out = out + "#" + "*" + "#";
You can use string.insert(pos, newString). Example below:
std::string mystr
mystr.insert(6,str2);
If you know the index, specify it directly as 'pos'. Otherwise, you may want to do somekind of str.find() and pass in the result.
If I have understood correctly what you need then you can use the following straightforward approach
#include <iostream>
#include <string>
#include <cstring>
int main()
{
char in[] = "ironman";
char symbols[] = "*%##^!";
std::string s;
s.reserve( std::strlen( in ) + std::strlen( symbols ) );
char *p = in;
char *q = symbols;
while ( *p && *q )
{
s.push_back( *p++ );
s.push_back( *q++ );
}
while ( *p ) s.push_back( *p++ );
while ( *q ) s.push_back( *q++ );
std::cout << s << std::endl;
}
The program output is
i*r%o#n#m^a!n
You can write a separate function. For example
#include <iostream>
#include <string>
#include <cstring>
std::string interchange_merge( const char *s1, const char *s2 )
{
std::string result;
result.reserve( std::strlen( s1 ) + std::strlen( s2 ) );
while ( *s1 && *s2 )
{
result.push_back( *s1++ );
result.push_back( *s2++ );
}
while ( *s1 ) result.push_back( *s1++ );
while ( *s2 ) result.push_back( *s2++ );
return result;
}
int main()
{
char in[] = "ironman";
char symbols[] = "*%##^!";
std::string s = interchange_merge( in, symbols );
std::cout << s << std::endl;
}
The output will be the same as above
i*r%o#n#m^a!n
When i am using StrSplit function with a constant string it works great. but when i read the line from a file and then use it as string for StrSplit function my program crashes. the following is the code and the error screen shot.
string Read_from_file(){
//load the text file and put it into a single string:
std::ifstream in("test.txt");
std::stringstream buffer;
buffer << in.rdbuf();
std::string test = buffer.str();
std::cout << test << std::endl << std::endl;
return test;
}
// split the given string according to give delimeters
vector<string> &StrSplit( string inStr, string sepStr, vector<string> &outStrVec) {
char * comp;
char * buffer = new char[ strlen( inStr.c_str() ) ];
strcpy( buffer, inStr.c_str() );
comp = strtok ( buffer, sepStr.c_str() );
while ( comp != NULL ) {
outStrVec.push_back(comp);
comp = strtok ( NULL, sepStr.c_str() );
}
delete[] comp;
delete[] buffer;
return outStrVec;
}
vector<string> StrSplit( string inStr, string sepStr ) {
vector<string> outStrVec;
return StrSplit( inStr, sepStr, outStrVec );
![enter image description here][2]}
int main( ) {
Read_from_file();
string fileinput = Read_from_file();
vector<string> v;
string inStr = fileinput;
v = StrSplit( inStr, "|#$ *#.&\"!^01" );
for (unsigned int i = 0; i < v.size(); ++i) {
cout << v[i] << '\n';
}
}
The buffer is too small to contain the string including the terminating 0 character.
Change
char * buffer = new char[ strlen( inStr.c_str() ) ];
to
char * buffer = new char[ strlen( inStr.c_str() ) + 1];
And don't call delete on pointers you didn't new (remove delete[] comp).
Actually, I would never use strtok in C++.
#include <stdio.h>
#include <dirent.h>
#include <sys/types.h>
#include <sys/param.h>
#include <sys/stat.h>
#include <unistd.h>
#include <string.h>
#include <stdlib.h>
#include <limits.h>
#include <vector>
using namespace std;
enum ElementType { SIMPLEFILE, DIRECTORY, SYMBOLICLINK };
class Element{
public:
Element(){};
Element(char *name_, char *full_path_name_, ElementType element_type_, long element_size_)
: name(NULL), full_path_name(NULL), element_size(0)
{
memcpy (name,name_,strlen(name_)+1);
memcpy (full_path_name,full_path_name_,strlen(full_path_name_)+1);
element_type=element_type_;
element_size=element_size_;
};
char *name;
char *full_path_name;
ElementType element_type;
long element_size;
};
int inspect(const char *input_path, std::vector<Element>& result_element_array, long *dir_size,const char *full_path ) {
std::vector<Element> result_element_array_temp;
DIR *d;
struct dirent *dir;
struct stat buf;
char *mynamebuf=(char *)malloc(0);
int c=0;
size_t base_len = strlen(full_path);
long dir_size_temp=0;
char *full_path_temp=(char *)malloc(0);
char *full_path_dummy=(char *)malloc(0);
result_element_array_temp.reserve(1000);
d = opendir( full_path);
if( d == NULL ) {
return 1;
}
while( ( dir = readdir( d ) )) {
if( strcmp( dir->d_name, "." ) == 0 || strcmp( dir->d_name, ".." ) == 0 ) {
continue;
}
memcpy (mynamebuf,full_path,strlen(full_path)+1);
strcat(mynamebuf,(full_path[base_len - 1] == '/' ? "" : "/"));
strcat(mynamebuf,dir->d_name);
if (stat(mynamebuf, &buf) != 0) {
perror(mynamebuf);
continue;
}
if( S_ISDIR(buf.st_mode) ) {//if dir
chdir( dir->d_name );
memcpy (full_path_temp,full_path,strlen(full_path)+1);
strcat(full_path_temp,"/");
strcat(full_path_temp,dir->d_name);
(dir_size_temp)=0;
inspect( ".", result_element_array_temp, &dir_size_temp, full_path_temp );
chdir( ".." );
memcpy (full_path_dummy,full_path_temp,strlen(full_path_temp)+1);
strcat(full_path_dummy,"/");
strcat(full_path_dummy,dir->d_name);
Element element;
element.name=dir->d_name;
element.full_path_name=full_path_dummy;
element.element_type=DIRECTORY;
element.element_size=dir_size_temp;
result_element_array.push_back(element);
result_element_array.insert( result_element_array.end(), result_element_array_temp.begin(), result_element_array_temp.end() );
(*dir_size)+=(dir_size_temp);
} else if( S_ISREG(buf.st_mode)) {//if file
memcpy (full_path_dummy,full_path,strlen(full_path)+1);
strcat(full_path_dummy,"/");
strcat(full_path_dummy,dir->d_name);
Element element;
element.name=dir->d_name;
element.full_path_name=full_path_dummy;
element.element_type=SIMPLEFILE;
element.element_size=buf.st_size;
result_element_array.push_back(element);
(*dir_size)+=buf.st_size;
} else if( S_ISLNK(buf.st_mode) ) {//if link
memcpy (full_path_dummy,full_path,strlen(full_path)+1);
strcat(full_path_dummy,"/");
strcat(full_path_dummy,dir->d_name);
Element element;
element.name=dir->d_name;
element.full_path_name=full_path_dummy;
element.element_type=SIMPLEFILE;
element.element_size=buf.st_size;
result_element_array.push_back(element);
} else {
continue;
}
}
closedir(d);
return 0;
}
int main(){
std::vector<Element> result_element_array;
result_element_array.reserve(3000);
long dir_size;
const char *full_path="/";
inspect("/", result_element_array, &dir_size,full_path );
std::vector <Element>::iterator It;
for(It = result_element_array.begin(); It != result_element_array.end(); ++It){
printf("%s\n",(*It).full_path_name);
}
return 0;
}
The code is above. I aim to write directory explorer recursively.
I GDBed the code and it reaches return 0 when inspect method called recursively, but return 0 can not be executed. Might it be about stackoverflow caused by some other lines? Any idea would be appreciated.
On your Element constructor, you're memcpy-ing char arrays without allocating the destination char*. You should consider using std::string:
class Element{
public:
Element(){};
Element(char *name_,
char *full_path_name_,
ElementType element_type_,
long element_size_)
:name(name),
full_path_name(full_path_name_),
element_size(0)
{
element_type=element_type_;
element_size=element_size_;
};
std::string name;
std::string full_path_name;
ElementType element_type;
long element_size;
};
You are also performing a malloc of 0 bytes, and then use this pointer as if it was valid:
char *mynamebuf=(char *)malloc(0);
You should also consider using std::string, or at least fix it to allocate a valid amount of bytes.
Since you're using gdb I'm going to assume valgrind is available. Your use of memcpy is unsafe and valgrind should assist with finding the various memory corruption issues your program seems to have. Like Jeremy Friesner and fontanini both suggested, use std::string instead of naked char *.
I would like to write a function which takes an fstream, processes it, and fills information in a struct I supply in the second argument.
My problem is that I am confused how to use pointers and fstreams as I get debug errors:
Access violation writing location
0xcccccccc.
Here is the main function:
int main()
{
keyframe_struct kfstruct;
string ifile = "filename";
ifstream fin( ifile, ios::binary );
load_from_keyframe_file( fin, kfstruct );
fin.close();
cout << kfstruct.num_keyframes << endl;
return 0;
}
And here is the function I try to use for parsing the binary file and filling in the information in the struct kfstruct:
struct keyframe_struct
{
int num_views;
int num_keyframes;
vector<keyframe> keyframes;
};
int load_from_keyframe_file( ifstream &fin, keyframe_struct &kfstruct )
{
char keyword[100];
while ( !fin.eof() )
{
fin.getline( keyword, 100, 0 );
if ( strcmp( keyword, "views" ) == 0 )
{
fin.read(( char* ) kfstruct.num_views, sizeof( int ) );
}
else if ( strcmp( keyword, "keyframes" ) == 0 )
{
fin.read(( char* ) kfstruct.num_keyframes, sizeof( int ) );
}
}
}
Can you tell me what am I doing wrong? I'm sure I am making some huge errors here as I am just a beginner and I still don't understand clearly what should I and what should I not do with pointers.
You forgot to take the address of your fields:
fin.read(( char* ) &kfstruct.num_views, sizeof( int ) );
^^^
[As an aside, note that it's better from a maintenance point of view to do sizeof(kfstruct.num_views). So if the type ever changes, your code will still work.]
Instead of
( char* ) kfstruct.num_views
use
( char* ) (&kfstruct.num_views)
similarly in the other place.
otherwise you are writing to a location whose address is equal to the VALUE of your int. You don't want that. You want the address converted to char*. You take the address by '&' operator.