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 11 months ago.
Improve this question
consider code:
using std::cout; using std::cerr;
using std::endl; using std::string;
using std::vector;
// . . .
char* envp[10];
vector<string> lines;
char* c_line = nullptr;
size_t len = 0;
while ((getline(&c_line, &len, input_file)) != -1) {
string line;
lines.push_back(line.assign(c_line));
}
fclose(input_file);
free(c_line);
sprintf(envp[0], "SERVER=%s", lines[0].data());
// printing envp[0] here OK
sprintf(envp[1], "DOMAIN=%s", lines[1].data());
// printing envp[1] never happened - Segmentation fault prints in output instead
I am C# dev I havent used C for couple decades. Something obvious is missing. Mem allocation?
P.S. I am mixing "old" char * for strings with std strings as the app uses 3rd party dll with chars*
EDIT declaring char envp[10][512]; fails down the line when I try to assing to the 3rd party property somedllObj.envps = envp; with cannot convert char[10][512] to char**
I do not recommend mixing std::string and old C-strings in such a wild manner. Instead I'd rely on C++ classes as long as possible:
std::ifstream inputFile("path to file");
if(!inputFile)
{
// error handling
}
std::vector<std::string> lines;
std::string tmp;
while(std::getline(inputFile, tmp))
{
lines.emplace_back(std::move(tmp)); // since C++11, not copying the strings...
}
if(!inputFile.eof())
{
// not the entire file read
// -> error handling!
}
// TODO: size check; what, if not sufficient lines in file?
lines[0] = std::string("SERVER=") + lines[0];
lines[1] = std::string("DOMAIN=") + lines[1];
std::vector<char*> envp;
envp.reserve(lines.size());
for(auto& l : lines) // C++11: range based for loop...
{
// note that this ABSOLUTELY requires the library not
// modifying the strings, otherwise undefined behaviour!
envp.emplace_back(const_cast<char*>(l.c_str()));
// UPDATE: this works as well:
envp.emplace_back(l.data());
// the string is allowed to be modified, too – apart from
// the terminating null character (undefined behaviour!)
}
// so far, pure C++...
// now we just get the pointers out of the vector:
libraryFunction(envp.size(), envp.data());
Bonus: No manual memory management at all...
(Note: Assuming the strings are not modified in the library!)
Assuming servers and domains are in the input file, they do not have whitespaces, thus getline is not necessary.
vector<string> lines;
std::ifstream input_file("path to file");
std::copy(std::istream_iterator<std::string>(input_file),
std::istream_iterator<std::string>(),
std::back_inserter(lines));
lines[0] = "SERVER="s + lines[0];
lines[1] = "DOMAIN="s + lines[1];
std::vector<char*> envp;
for(auto& line : lines)
envp.emplace_back(line.data());
envp.emplace_back(nullptr); // Usually env arrays end with nullptr, thus +1 to array size
somedllObj.envps = envp.data();
Related
I have a text file with the following information in it:
2B,410,AER,2965,KZN,2990,,0,CR2
2B,410,ASF,2966,KZN,2990,,0,CR2
2B,410,ASF,2966,MRV,2962,,0,CR2
2B,410,CEK,2968,KZN,2990,,0,CR2
2B,410,CEK,2968,OVB,4078,,0,CR2
2B,410,DME,4029,KZN,2990,,0,CR2
2B,410,DME,4029,NBC,6969,,0,CR2
2B,410,DME,4029,TGK,\N,,0,CR2
(it is airline route info)
I'm trying to loop through the file and extract each line into a char* - simple right?
Well, yes, it's simple but not when you've completely forgotten how to write successful i/o operations! :)
My code goes a little like:
char * FSXController::readLine(int offset, FileLookupFlag flag)
{
// Storage Buffer
char buffer[50];
std::streampos sPos(offset);
try
{
// Init stream
if (!m_ifs.is_open())
m_ifs.open(".\\Assets\\routes.txt", std::fstream::in);
}
catch (int errorCode)
{
showException(errorCode);
return nullptr;
}
// Set stream to read input line
m_ifs.getline(buffer, 50);
// Close stream if no multiple selection required
if (flag == FileLookupFlag::single)
m_ifs.close();
return buffer;
}
Where m_ifs is my ifStream object.
The problem is that when I breakpoint my code after the getline() operation, I notice that 'buffer' has not changed?
I know it is something simple, but please could someone shed some light onto this - I'm tearing my forgetful hair out! :)
P.S: I never finished writing the exception handling so it is pretty useless right now!
Thanks
Here is a fix with some important c++ libraries you may want to learn, and what I believe a better solution. Since you just need your final result to be strings:
// A program to read a file to a vector of strings
// - Each line is a string element of a vector container
#include <fstream>
#include <string>
#include <vector>
// ..
std::vector<std::string> ReadTheWholeFile()
{
std::vector<std::string> MyVector;
std::string JustPlaceHolderString;
std::ifstream InFile;
InFile.open("YourText.txt"); // or the full path of a text file
if (InFile.is_open())
while (std::getline(InFile, PlaceHolderStr));
MyVector.push_back(PlaceHolderStr);
InFile.close(); // we usually finish what we start - but not needed
return MyVector;
}
int main()
{
// result
std::vector<std::string> MyResult = ReadTheWholeFile();
return 0;
}
There are two basic problems with your code:
You are returning a local variable. The statement return buffer; results in a dangling pointer.
You are using a char buffer. C-style strings are discouraged in c++, you should always prefer std::string instead.
A far better approach is this:
string FSXController::readLine(int offset, FileLookupFlag flag) {
string line;
//your code here
getline(m_ifs, line) //or while(getline(my_ifs, line)){ //code here } to read multiple lines
//rest of your code
return line;
}
More information about std::string can be found here
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 6 years ago.
Improve this question
I'm trying to save data from a file into an array but I've had no luck for now. It's easy to save data after it was read from the file in case it's just numbers, but for example if I'm trying to save strings, the program keeps crashing over and over again. I'm using fscanf(); function since the whole .txt file is written in the same format: "First Name, Last Name". Now then, I've tried using the for loop in this way:
char *firstName = (char*)malloc(sizeof(char)*10240);
char *lastName = (char*)malloc(sizeof(char)*10240);
for(int i = 0; i<10; i++){
fscanf(fp, "%s %s", firstName[i],lastName[i]);
}
And that's where it crashes.
Pure C code:
You have to allocate the array of arrays first, then allocate each string one by one
It's best to scan the strings into temp strings with a big size, and duplicate the strings later.
int i,nb_names = 10;
char **firstName = malloc(sizeof *firstName * nb_names);
char **lastName = malloc(sizeof *lastName *nb_names);
char tempn[1000],templ[1000];
for(i = 0; i<nb_names; i++){
fscanf(fp,"%s %s", tempn,templ);
firstName[i] = strdup(tempn);
lastName[i] = strdup(templ);
}
Note that I have changed for (int i to for (i because it is not C compliant but rather C++ compliant (or C99, not sure).
For C++, drop the mallocs and use std::vector and std:string instead.
I'd recommend to use C++ if you can. I answered a lot of C/C++ questions on people trying (and failing) to allocate 2D arrays properly (including me 5 minutes ago damn :)). C++ using C++ library code is much clearer.
Full C++ example, reading from standard input
#include <vector>
#include <string>
#include <iostream>
using namespace std;
int main()
{
int nb_names = 10;
vector<string > firstName(nb_names);
vector<string > lastName(nb_names);
for(int i = 0; i<nb_names; i++){
cin >> firstName[i];
cin >> lastName[i];
}
return 0;
}
The error in your code is : firstName[i] is a character not a string but you use it like a string by using %s instead of %c.
you should use a char ** instead of char *.
char **firstName = (char**)malloc(10*sizeof(char)*10240);
I think also that 10240 is too much for firstName. Use 255 or less.
This question already has answers here:
Closed 10 years ago.
Possible Duplicate:
Splitting a string in C++
I need a function of split.
has to work like this:
buffer = split(str, ' ');
I searchead a split functions, tryed boost libs, and all works bad :/
strtok() from standard c library is pretty good and does what you are looking for. Unless you are keen on using it from multiple threads and worried about function not being re entrant which i don't suspect is the case here.
P.S. Above assumes you have a character array as input. If it was a c++ string, still you can use string.c_str to get the c string before using strtok
The boost lib is supposed to work as well.
Use it like so:
vector <string> buffer;
boost::split(buffer, str_to_split, boost::is_any_of(" "));
Added:
Make sure to include the algorithm:
#include <boost/algorithm/string.hpp>
Print it to the std::cout like so:
vector<string>::size_type sz = buffer.size();
cout << "buffer contains:";
for (unsigned i=0; i<sz; i++)
cout << ' ' << buffer[i];
cout << '\n';
I guess strtok() is what you're looking for.
It allows you to always return the first sub string delimited by given character(s):
char *string = "Hello World!";
char *part = strtok(string, " "); // passing a string starts a new iteration
while (part) {
// do something with part
part = strtok(NULL, " "); // passing NULL continues with the last string
}
Note that this version must not be used in several threads at once (there's also a version (strtok_s(), more details here) which has an additional parameter to make it work in a parallelized environment). This is also true for cases where you'd like to split a substring within a loop.
Right, please bear with me as I have two separate attempts I'll cover below.
I first started off reading the guide here (http://www.cplusplus.com/doc/tutorial/files/). However whilst it contains what appears to be a good example of how to use read(), it does not contain an example of how to use write().
I first attempted to store a simple char array in binary using write(). My original idea (and hope) was that I could append to this file with new entries using ios::app. Originally this appeared to work, but I was getting junk output as well. A post on another forum for help suggested I lacked a null terminator on the end of my char array. I applied this (or at least attempted to based on how I was shown) as can be seen in the example below. Unfortunately, this meant that read() no longer functioned properly because it won't read past the null terminator.
I was also told that doing char *memoryBlock is 'abuse' of C++ standard or something, and is unsafe, and that I should instead define an array of an exact size, ie char memoryBlock[5], however what if I wish to write char data to a file that could be of any size? How do I proceed then? The code below includes various commented out lines of code indicating various attempts I have made and different variations, including some of the suggestions I mentioned above. I do wish to try and use good-practice code, so if char *memoryBlock is unsafe, or any other lines of code, I wish to amend this.
I would also like to clarify that I am trying to write chars here for testing purposes only, so please do not suggest that I should write in text mode rather than binary mode instead. I'll elaborate further in the second part of this question under the code below.
First code:
#include <cstdlib>
#include <iostream>
#include <fstream>
//#include <string>
int main()
{
//char memoryBlock[5];
char *memoryBlock;
char *memoryBlockTwo;
std::ifstream::pos_type size;// The number of characters to be read or written from/to the memory block.
std::ofstream myFile;
myFile.open("Example", std::ios::out | /*std::ios::app |*/ std::ios::binary);
if(myFile.is_open() && myFile.good())
{
//myFile.seekp(0,std::ios::end);
std::cout<<"File opening successfully completed."<<std::endl;
memoryBlock = "THEN";
//myFile.write(memoryBlock, (sizeof(char)*4));
//memoryBlock = "NOW THIS";
//strcpy_s(memoryBlock, (sizeof(char)*5),"THIS");
//memoryBlock = "THEN";
//strcpy(memoryBlock, "THIS");
//memoryBlock[5] = NULL;
myFile.write(memoryBlock, (sizeof(char)*5));
}
else
{
std::cout<<"File opening NOT successfully completed."<<std::endl;
}
myFile.close();
std::ifstream myFileInput;
myFileInput.open("Example", std::ios::in | std::ios::binary | std::ios::ate);
if(myFileInput.is_open() && myFileInput.good())
{
std::cout<<"File opening successfully completed. Again."<<std::endl;
std::cout<<"READ:"<<std::endl;
size = myFileInput.tellg();
memoryBlockTwo = new char[size];
myFileInput.seekg(0, std::ios::beg);// Get a pointer to the beginning of the file.
myFileInput.read(memoryBlockTwo, size);
std::cout<<memoryBlockTwo<<std::endl;
delete[] memoryBlockTwo;
std::cout<<std::endl<<"END."<<std::endl;
}
else
{
std::cout<<"Something has gone disasterously wrong."<<std::endl;
}
myFileInput.close();
return 0;
}
The next attempt of mine works on the basis that attempting to use ios::app with ios::binary simply won't work, and that to ammend a file I must read the entire thing in, make my alterations, then write back and replace the entire contents of the file, although this does seem somewhat inefficient.
However I don't read in and ammend contents in my code below. What I am actually trying to do is write an object of a custom class to a file, then read it back out again intact.
This seems to work (although if I'm doing anything bad code-wise here, please point it out), HOWEVER, I am seemingly unable to store variables of type std::string and std::vector because I get access violations when I reach myFileInput.close(). With those member variables commented out the access violation does not occur. My best guess as to why this happens is that They use pointers to other pieces of memory to store their files, and I am not writing the data itself to my file but the pointers to it, which happen to still be valid when I read my data out.
Is it possible at all to store the contents of these more complex datatypes in a file? Or must I break everything down in to more basic variables such as chars, ints and floats?
Second code:
#include <cstdlib>
#include <iostream>
#include <fstream>
#include <string>
#include <vector>
class testClass
{
public:
testClass()
{
testInt = 5;
testChar = 't';
//testString = "Test string.";
//testVector.push_back(3.142f);
//testVector.push_back(0.001f);
}
testClass(int intInput, char charInput, std::string stringInput, float floatInput01, float floatInput02)
{
testInt = intInput;
testChar = charInput;
testArray[0] = 't';
testArray[1] = 'e';
testArray[2] = 's';
testArray[3] = 't';
testArray[4] = '\0';
//testString = stringInput;
//testVector = vectorInput;
//testVector.push_back(floatInput01);
//testVector.push_back(floatInput02);
}
~testClass()
{}
private:
int testInt;
char testChar;
char testArray[5];
//std::string testString;
//std::vector<float> testVector;
};
int main()
{
testClass testObject(3, 'x', "Hello there!", 9.14f, 6.662f);
testClass testReceivedObject;
//char memoryBlock[5];
//char *memoryBlock;
//char *memoryBlockTwo;
std::ifstream::pos_type size;// The number of characters to be read or written from/to the memory block.
std::ofstream myFile;
myFile.open("Example", std::ios::out | /*std::ios::app |*/ std::ios::binary);
if(myFile.is_open() && myFile.good())
{
//myFile.seekp(0,std::ios::end);
std::cout<<"File opening successfully completed."<<std::endl;
//memoryBlock = "THEN";
//myFile.write(memoryBlock, (sizeof(char)*4));
//memoryBlock = "NOW THIS";
//strcpy_s(memoryBlock, (sizeof(char)*5),"THIS");
//memoryBlock = "THEN AND NOW";
//strcpy(memoryBlock, "THIS");
//memoryBlock[5] = NULL;
myFile.write(reinterpret_cast<char*>(&testObject), (sizeof(testClass)));//(sizeof(char)*5));
}
else
{
std::cout<<"File opening NOT successfully completed."<<std::endl;
}
myFile.close();
std::ifstream myFileInput;
myFileInput.open("Example", std::ios::in | std::ios::binary | std::ios::ate);
if(myFileInput.is_open() && myFileInput.good())
{
std::cout<<"File opening successfully completed. Again."<<std::endl;
std::cout<<"READ:"<<std::endl;
size = myFileInput.tellg();
//memoryBlockTwo = new char[size];
myFileInput.seekg(0, std::ios::beg);// Get a pointer to the beginning of the file.
myFileInput.read(reinterpret_cast<char *>(&testReceivedObject), size);
//std::cout<<memoryBlockTwo<<std::endl;
//delete[] memoryBlockTwo;
std::cout<<std::endl<<"END."<<std::endl;
}
else
{
std::cout<<"Something has gone disasterously wrong."<<std::endl;
}
myFileInput.close();
return 0;
}
I apologise for the long-windedness of this question, but I am hoping that my thoroughness in providing as much information as I can about my issues will hasten the appearance of answers, even for this (what may even be a simple issue to fix although I have searched for hours trying to find solutions), as time is a factor here. I will be monitoring this question throughout the day to provide clarifications in the aid of an answer.
In the first example, I'm not sure what you are writing out as memoryBlock is commented out and never initialized to anything. When you are reading it in, since you are using std::cout to display the data to the console, it MUST be NULL terminated or you will print beyond the end of the memory buffer allocated for memoryBlockTwo.
Either write the terminating null to the file:
memoryBlock = "THEN"; // 4 chars + implicit null terminator
myFile.write(memoryBlock, (sizeof(char)*5));
And/or, ensure the buffer is terminated after it is read:
myFileInput.read(memoryBlockTwo, size);
memoryBlockTwo[size - 1] = '\0';
In your second example, don't do that with C++ objects. You are circumventing necessary constructor calls and if you try that using vectors like you have commented out it certainly won't work like you expect. If the class is plain old data (non-virtual functions, no pointers to other data) you will likely be OK, but it's still really bad practice. When persisting C++ objects, consider looking into overloading the << and >> operators.
I'm almost finished with my program, but there's one last bug that I'm having problems ferreting out. The program is supposed to check about 10 scrambled words against a wordlist to see what the scrambled words are anagrams of. To do this, I alphabetized each word in the wordlist (apple would become aelpp), set that as the key of a map, and made the corresponding entry the original, unalphabetized word.
The program is messing up when it comes to the entries in the map. When the entry is six characters or less, the program tags a random character on the end of the string. I've narrowed down what can be causing the problem to a single loop:
while(myFile){
myFile.getline(str, 30);
int h=0;
for (; str[h] != 0; h++)//setting the initial version of str
{
strInit[h]=str[h]; //strInit is what becomes the entry into the map.
}
strInit[h+1]='\0'; //I didn't know if the for loop would include the null char
cout<<strInit; //Personal error-checking; not necessary for the program
}
And if it's necessary, here's the entire program:
Program
Prevent issues, use normal functions:
getline(str, 30);
strncpy(strInit, str, 30);
Prevent more issues, use standard strings:
std::string strInit, str;
while (std::getline(myFile, str)) {
strInit = str;
// do stuff
}
Best not to use raw C arrays at all! Here's a version, using modern C++:
#include <string>
std::string str;
while (std::getline(myFile, str))
{
// do something useful with str
// Example: mymap[str] = f(str);
std::cout << str; //Personal error-checking; not necessary for the program
}