This question already has answers here:
Returning char* from function not working
(3 answers)
Closed 4 years ago.
note: the title may not be relevant to the issue, because I can't understand where the issue comes from.
The isuue: I have a piece of code which gets a filename from a dir and appends it to a vector < char* >.
It works ok, but if I wrap it in a function, it gives weird behavior, the resulting vector element prints single random character in console instead of the filename.
I have checked and re-checked everything but can't see why it is happening.
Below is complete runnable code, I compiled it on Windows with cl.exe
namely just cl.exe "a.cpp" /EHsc. x64 native environment and target.
I put together both pieces, for easier testing.
#include <windows.h>
#include <iostream>
#include <vector>
using namespace std;
void getimglist ( const char* mask, vector < char* > & flist )
{
WIN32_FIND_DATA data;
HANDLE hFind;
hFind = FindFirstFile ( mask, & data );
cout << "-fname: " << data.cFileName << "\n";
flist.push_back ( data.cFileName );
FindClose ( hFind );
}
int main (int argc, char* argv[])
{
vector < char* > L ;
vector < char* > L2 ;
const char* mask = ".\\input-cam0\\*.jpg";
const char* mask2 = ".\\input-cam0\\*.jpg";
// this code works
WIN32_FIND_DATA data;
HANDLE hFind;
hFind = FindFirstFile ( mask, & data );
cout << " first file: " << data.cFileName << "\n";
L.push_back ( data.cFileName );
FindClose (hFind);
// ***
cout << " size:" << L.size() << "\n";
cout << " first file L:" << L[0] << "\n";
// this works weird, output is different and wrong
cout << " ** function call **\n";
getimglist ( mask2, L2 );
cout << " size:" << L2.size() << "\n";
cout << " first file: " << L2[0] << "\n";
return 0;
} // end main
Output:
first file: 000-001.jpg
size:1
first file L:000-001.jpg
** function call **
-fname: 000-001.jpg
size:1
first file: R
See the last line ^, here it is R, and if I run multiple times the exe file, it prints single random character. And the result from main block gives correct results. Where is the problem?
WIN32_FIND_DATA data is allocated on the stack, stack memory is destroyed when your variable goes out of scope (in this case at the end of the function). This means that the pointer you have stored in your vector doesn't point to a valid address any more. When you print it the application happily prints whatever is now in the specified memory segment.
The simple fix is to copy the string into a new variable, the preferred way to do this would be to change your vectors into vector<std::string>. You can still call flist.push_back ( data.cFileName ) and the compiler will copy the character array into a std::string for you.
Related
I wanna compare value that stored in filename[i] and filename[j] and print out the value in filename[i] that do not have the same filename as in filename[j]. I know it is possible to do using set_difference and sort solution but I do not know exactly to write the sort and set_differences code. Here i provide my original code so that u can test it out and more understand what I'm trying to do.
my full code:
#include <string>
#include <iostream>
#include <ctime> //important when to make random filename- srand(time(0))
#include <opencv2\opencv.hpp> //important when using opencv
#include <vector> //when using vector function
using namespace std;
using namespace cv; //important when using opencv
int main(int argc, char* argv[]) {
vector<String> filenames;
int a, i;
srand(time(0)); //seed random filenames - for random filename
// Get all jpg in the folder
cv::glob("C:\\Users\\x\\Documents\\Aggressive\\abc", filenames);
for (size_t i = 0; i < filenames.size(); i++)
{
Mat im = imread(filenames[i]); //read the filename location
std::cout << "\n";
std::size_t found = filenames[i].find_last_of("//\\");
//std:cout << " file: " << filenames[j].substr(found + 1) << '\n'; //display filename and its format (.jpg)
std::string::size_type const p(filenames[i].substr(found + 1).find_last_of('.')); //eg: 2.jpg then it will find the last '.'
std::string file_without_extension = filenames[i].substr(found + 1).substr(0, p); //eg: 2
std::cout << " file : " << filenames[i].substr(found + 1).substr(0, p); //display filename without .jpg
}
cout << "\n";
cout << "There's " << filenames.size() << " files in the current directory.\n" << endl; // total file in the specific directory
cout << "Enter array size: \n";
cin >> a;
for (int j = 0; j < filenames.size(); j++) {
//generate random filename
int index = rand() % filenames.size(); //random based on total of the file in the directory
//cout << filenames[index] << endl; //display the random number but might be redundant
//swap filenames[j] with filenames[index]
string temp = filenames[j];
filenames[j] = filenames[index];
filenames[index] = temp;
}
for (int j = 0; j < a; j++) {
//cout << "Random image selected:" << filenames[j] << endl; //basically to avoid the redundant random filename
Mat im = imread(filenames[j]); //read filename location
std::size_t found = filenames[j].find_last_of("//\\");
//std:cout << " file: " << filenames[j].substr(found + 1) << '\n'; //display filename and its format (.jpg)
std::string::size_type const p(filenames[j].substr(found + 1).find_last_of('.')); //eg: 2.jpg then it will find the last '.'
std::string file_without_extension = filenames[j].substr(found + 1).substr(0, p); //eg: 2
std::cout << " file: " << filenames[j].substr(found + 1).substr(0, p); //display filename without .jpg
string written_directory = "C:/Users/x/Documents/folder/" + filenames[j].substr(found + 1).substr(0, p) + ".jpg"; // write filename based on its original filename.
imwrite(written_directory, im);
}
return 0;
}
In my opinion this is a perfect example of an XY Problem. From you question, from your code and even from the comments, people do not really understand what you want to do. With that I mean, what do you want to achieve?
It is a vague guess that you want to copy a specified number of random selected JPEG files from one directory to the other. And that you want to show the filenames of the files that will not be copied.
Let me give you some examples, what is the reason for all this confusion.
First and most important, you do not show the full code. Definitions and variable types and functions are misssing an. This is also not a Minimum, Reproducable Example. And the description in your question is hard to understand.
I have two set of array
You have "two set array"? Do you mean, you have 2 [std::set][3] of [std::array][3]. Or maybe you have simply 2 [std::vector][3] of std::string. From what we can see in the code, we could assume a std::vector<std::string>>, but we do not know, because you did not show the feinition of "filenames".
Then, you are talking about "2" something. But we do see only one "filenames". So, 2 or 1?
in a comment you are writing
in the array 2 i had a random filename based on the size of array that an user entered
My guess is that you do not want to have a random filename, but you want to select filenames with a random index from the first vector and put it into a 2nd vector? But we can see only 1 vector "filenames" where you do some random swapping activity.
Then you have written
imread is actually to read the whole file in the folder of directory
This function is very important, what does it do? And what do you mean by "read the file"? Do you mean "filename", so the name of the file? Or the contents of the file? And what is the meaning of "folder of directory"? All filenames in one folder? Or subfolder of a directory entry?
So now my objective is to print out all the file that do not have same filename in the array 2
Again, do we really have 2 arrays(vector)? are they different?
And then, where do you copy the files?
So, you see, it is very hard to understand. Even, if people would like to help you, they cannot, because they do not understand you. Better to show a link to your original home work. Then people can help you. Members here on Stack Overflow want to help. But please allow them to do so.
Here I give you an abstract example for the random selection problem and set_difference problem:
#include <iostream>
#include <vector>
#include <string>
#include <algorithm>
#include <random>
int main() {
// Define 2 Vectors for filenames
// This vector is an example for files that could be in a specified directory
std::vector<std::string> fileNamesInDirectory{"8.jpg","5.jpg", "6.jpg", "9.jpg", "1.jpg", "4.jpg", "2.jpg", "3.jpg", };
// Print the filenames as information for the user
for (size_t i = 0U; i < fileNamesInDirectory.size(); ++i) {
std::cout << fileNamesInDirectory[i] << "\n";
}
// Next: Select randomly a given number of filenames from the above vector
// So, first get the number of selections. Inform the user
std::cout << "\nEnter a number of filenames that should be copied randomly. Range: 1-"<< fileNamesInDirectory.size()-1 << "\t";
size_t numberOfSelectedFileNames{};
std::cin >> numberOfSelectedFileNames;
// Check for valid range
if (numberOfSelectedFileNames == 0 || numberOfSelectedFileNames >= fileNamesInDirectory.size()) {
std::cerr << "\n*** Error. Wrong input '" << numberOfSelectedFileNames << "'\n";
}
else {
// Copy all data from fileNamesInDirectory
std::vector<std::string> selection{ fileNamesInDirectory };
// Shuffle the data randomly: Please see here: https://en.cppreference.com/w/cpp/algorithm/random_shuffle
std::random_device rd;
std::mt19937 g(rd());
std::shuffle(selection.begin(), selection.end(), g);
// Resize to the number, given by the user
selection.resize(numberOfSelectedFileNames);
// Now we have a random list of filenames
// Show, what we have so far. Now, because we are learning, we will use the range based for
std::cout << "\n\nOriginal file names:\n";
for (const std::string& s : fileNamesInDirectory) std::cout << s << "\n";
std::cout << "\n\nRandomly selected file names:\n";
for (const std::string& s : selection) std::cout << s << "\n";
// Sort both vectors
std::sort(fileNamesInDirectory.begin(), fileNamesInDirectory.end());
std::sort(selection.begin(), selection.end());
// Show again to the user:3
std::cout << "\n\nOriginal file names sorted:\n";
for (const std::string& s : fileNamesInDirectory) std::cout << s << "\n";
std::cout << "\n\nRandomly selected file names sorted:\n";
for (const std::string& s : selection) std::cout << s << "\n";
// Now, find out the difference of both vectors, meaning, what will not be selected and later copied
std::vector<std::string> difference{};
// Calculate the difference with a std::algorithm: https://en.cppreference.com/w/cpp/algorithm/set_difference
std::set_difference(fileNamesInDirectory.begin(), fileNamesInDirectory.end(), selection.begin(), selection.end(), std::back_inserter(difference));
std::cout << "\n\nThe following file names have not been selected:\n";
for (const std::string& s : difference) std::cout << s << "\n";
}
return 0;
}
If you are more advanced then you can and will use functions from the C++ filesystem library. That will make life easier . . .
I want to read a chunk of data from file into stringstream, which later will be used to parse the data (using getline, >>, etc). After reading the bytes, I set the buffer of the stringstream, but I cant make it to set the p pointer.
I tested the code on some online services, such as onlinegdb.com and cppreference.com and it works. However, on microsoft, I get an error - the pointers get out of order.
Here's the code, I replaced the file-read with a char array.
#include <sstream>
#include <iostream>
int main()
{
char* a = new char [30];
for (int i=0;i<30;i++)
a[i]='-';
std::stringstream os;
std::cout << "g " << os.tellg() << " p " << os.tellp() << std::endl;
os.rdbuf()->pubsetbuf(a,30);
os.seekp(7);
std::cout << "g " << os.tellg() << " p " << os.tellp() << std::endl;
}
the output I get when it works
g 0 p 0
g 0 p 7
the output I get on visual studio 2015
g 0 p 0
g -1 p -1
any ides?
thanks
std::sstream::setbuf may do nothing:
If s is a null pointer and n is zero, this function has no effect.
Otherwise, the effect is implementation-defined: some implementations do nothing, while some implementations clear the std::string member currently used as the buffer and begin using the user-supplied character array of size n, whose first element is pointed to by s, as the buffer and the input/output character sequence.
You are better off using the std::stringstream constructor to set the data or call str():
#include <sstream>
#include <iostream>
int main()
{
std::string str( 30, '-' );
std::stringstream os;
std::cout << "g " << os.tellg() << " p " << os.tellp() << std::endl;
os.str( str );
os.seekp(7);
std::cout << "g " << os.tellg() << " p " << os.tellp() << std::endl;
}
I am a newbie, writing a c++ code to open and read from multiple files and then dump part of the data into other files.
I want to generate file names in a for loop.
But I can't concatenate string(numbering of file) and string literal(a file extension). The same line of code works at the very beginning of the program, but not at the later part.
int main(int argc, char *argv[])
{
std::cout << std::string("9") + ".dat" << std::endl;
// many more lines
dump = 1;
if (dump == 1){
for (int ilevel=std::max(levelmin,lmin); ilevel < lmax + 1; ilevel++){
std::cout << std::string("9") + ".dat" << std::endl; // crashes here!
std::ofstream fout (std::string("9") + ".dat", std::ios::out | std::ios::binary);
std::cout << grid[ilevel].cube[0] << std::endl;
fout.write ((char*)&grid[ilevel].cube[0], grid[ilevel].cube.size() * sizeof(grid[ilevel].cube[0]));
fout.close();
}
}
...
}
If I put std::cout << std::string("9") + ".dat" << std::endl; at the beginning, it works and prints "9.dat".
but in the later loop, segmentation fault.
In between I call a function that uses stringstream to pad leading zeros to an integer. The function looks:
std::string int2str(const int n, const int m){
std::stringstream ss;
ss << std::setfill('0') << std::setw(m) << n;
std::string s2(ss.str());
ss.clear();
return s2;
}
I don't have a clear understanding about string and stringstream in c++.
But out of many things in my program, this function is the only thing I can think of being relevant. Other parts of codes does not deal with strings. It's mostly array manipulation code.
I've also tried std::string("9") + std::string(".dat")
but had no luck.
What is wrong?
Is there a specific reason why you're using std::string("9") rather than just "9"?
Where does the 9 come from? If its generated as part of a loop or a returned value from a function you can either place the variable itself to be concatenated, or the function that returns it, so:
std::cout << iFileNumber + ".dat" << std::endl;
or
std::cout << fileNumberGenerator() + ".dat" << std::endl;
For the hardcoded examples you've provided, I personally can't see the need for anything other than
std::cout << 9 + ".dat" << endl;
but that could easily just be lack of experience on my part.
For the sake of printing to the command line, its also worth nothing that this is equally acceptable syntax (assuming you're not already aware):
std::cout << 9 << ".dat" << endl;
I have the below code.
main()
{
test::RouteMessage *Rtmesg = new test::RouteMessage;
test::RouteV4Prefix *prefix = new test::RouteV4Prefix;
test::RouteMessage testRtmesg;
prefix->set_family(test::RouteV4Prefix::RT_AFI_V4);
prefix->set_prefix_len(24);
prefix->set_prefix(1000);
Rtmesg->set_routetype(test::RouteMessage::RT_TYPE_BGP);
Rtmesg->set_allocated_v4prefix(prefix);
Rtmesg->set_flags(test::RouteMessage::RT_FLGS_NONE);
Rtmesg->set_routeevnt(test::RouteMessage::BGP_EVNT_V4_RT_ADD);
Rtmesg->set_nexthop(100);
Rtmesg->set_ifindex(200); Rtmesg->set_metric(99);
Rtmesg->set_pref(1);
int size = Rtmesg->ByteSize();
char const *rt_msg = (char *)malloc(size);
google::protobuf::io::ArrayOutputStream oarr(rt_msg, size);
google::protobuf::io::CodedOutputStream output (&oarr)
Rtmesg->SerializeToCodedStream(&output);
// Below code is just to see if everything is fine.
google::protobuf::io::ArrayInputtStream iarr(rt_msg, size);
google::protobuf::io::CodedInputStream Input (&iarr)
testRtmesg.ParseFromCodedStream(&Input);
Vpe::RouteV4Prefix test_v4Prefix = testRtmesg.v4prefix();
cout << std::endl;
std::cout << "Family " << test_v4Prefix.family() << std::endl;
std::cout << "Prefix " << test_v4Prefix.prefix()<< std::endl;
std::cout << "PrefixLen " << test_v4Prefix.prefix_len() << std::endl;
// All the above outputs are fine.
cout << std::endl;
cout << rt_msg; <<------------ This prints absolutely junk.
cout << std::endl;
amqp_bytes_t str2;
str2 = amqp_cstring_bytes(rt_msg); <<----- This just crashes.
printf("\n str2=%s %d", str2.bytes, str2.len);
}
Any operation on the above rt_msg just crashes. I want to use the above buffer to send to socket and another rabbitmq publish APIs.
Anybody out there who had similar issue...or worked out similar code ?
Protocol Buffers is a binary serialization format, not text. This means:
Yes, if you write the binary data to cout, it will look like junk (or crash).
The data is not NUL-terminated like C strings. Therefore, you cannot pass it into a function like amqp_cstring_bytes which expects a NUL-terminated char* -- it may cut the data short at the first 0 byte, or it may search for a 0 byte past the end of the buffer and crash. In general, any function that takes a char* but does not also take a length won't work.
I'm not familiar with amqp, but it looks like the function you are trying to call, amqp_cstring_bytes, just builds a amqp_bytes_t, which is defined as follows:
typedef struct amqp_bytes_t_ {
size_t len;
void *bytes;
} amqp_bytes_t;
So, all you have to do is something like:
amqp_bytes_t str2;
str2.bytes = rt_msg;
str2.len = size;
I'm looking to read a text file using a relative path in C++. The directory structure is as follows: source -> Resources -> Settings -> Video.txt.
The contents of the file (note: these are used for testing, of course):
somes*** = 1
mores*** = 2
evenmores*** = 3
According to my research, this is possible. Still, I find that this has yet to work. For example, when I step through my debugger, my char *line variable which is used to receive line-by-line text file input is always at a constant 8 value. As to my understanding, a char pointer can act as a dynamic array of characters which you may reassign to.
Why can't I read my file? When I try do an if ( !videoSettings ), it returns true, and I get an error message (created by myself).
Code
#ifdef WIN32
const char *filePath = "Resources\\Settings\\Video.txt";
#else
const char *filePath = "Resources/Settings/Video.txt";
#endif
std::ifstream videoSettings( filePath );
if ( !videoSettings )
{
cout << "ERROR: Failed opening file " << filePath << ". Switching to configure mode." << endl;
//return false;
}
int count = 0;
char *line;
while( !videoSettings.eof() )
{
videoSettings >> line;
cout << "LOADING: " << *line << "; ";
count = sizeof( line ) / sizeof( char );
cout << "VALUE: " << line[ count - 1 ];
/*
for ( int i = count; i > count; --i )
{
if ( i == count - 4 )
{
}
}
*/
}
delete line;
Wow ok- you cannot read a string of text into just a char * you need to preallocate the memory first.
2nd the size of a char* pointer is constant - but the size of the data it points to is not
I suggest using the std::string getline call and avoid all the dynamic memory allocation
So this would be
std::ifstream in("file.txt");
std::string line;
while(getline(in, line))
{
std::cout << line << std::endl;
}
Lastly relative paths are the last of your problems in you code example :-)