Passing string through stat() function - c++

I have following code:
#include <stdio.h> // For printf()
#include <sys/stat.h> // For struct stat and stat()
struct stat stResult;
if(stat("Filename.txt", &stResult) == 0)
{
// We have File Attributes
// stResult.st_size is the size in bytes (I believe)
// It also contains some other information you can lookup if you feel like it
printf("Filesize: %i", stResult.st_size);
}
else
{
// We couldn't open the file
printf("Couldn't get file attributes...");
}
Now. How can I pass my own string through stat()? Like this
string myStr = "MyFile.txt";
stat(myStr, &stResult)

If you are talking about a C++ std::string, you might want to use
string myStr = "MyFile.txt";
stat(myStr.c_str(), &stResult)
so use the c_str() member-function of your string object in order to get a C string representation.

Use the c_str() member function.
myStr.c_str()
http://www.cplusplus.com/reference/string/string/c_str/

Related

External Objects Across CPP files (Boost Filesystem/Variants, Libconfini)

I've got a deceptively complex one for y'all.
I am attempting to make a program which makes use of an INI config file (I am using the libconfini C library), which looks like this:
[General]
output_directory = /scratch/miles/trans_assembly_pipeline/
[SRA accessions]
SRR18328591
SRR28481881
It parses the information therein into a map, index-able by section, and then by key, which looks like this (the only important bit really is the char* option in the variant object):
map<string, map<string, boost::variants<int, bool, double, string, char*>>
Since keys in INI files can have various types, I am utilizing Boost's 'Variants' library to allow for multiple value types. Finally, for convenient file management, I am using Boost's 'Filesystem' library.
I have an INI parsing implementation file, which basically just stores the INI data into that map type, defined thus (the header for which, merely containing function declarations and definition directives, can be found at the bottom):
#include "ini_parse.h"
namespace fs = boost::filesystem;
// Dispatch INI data to a map, indexable by sections, then by keys
static int ini_callback(IniDispatch * const dispatch, void * map_pt) {
#define thismap (reinterpret_cast<INI_MAP*>(map_pt))
if (dispatch->type == INI_COMMENT) {
return 0;
}
if (dispatch->type == INI_SECTION) {
INI_MAP_ENTRY newSec;
thismap->insert(std::pair<std::string, INI_MAP_ENTRY>(dispatch->data, newSec));
return 0;
}
if (dispatch->type == INI_KEY) {
(*thismap)[dispatch->append_to].insert(std::pair<std::string, BOOST_VAR>(dispatch->data, dispatch->value));
}
return 0;
}
// Given a FILE object, return a buffer containing its contents
char * make_ini_buffer(FILE * iniFile) {
char * iniBuffer;
long sz;
fseek(iniFile, 0L, SEEK_END);
sz = ftell(iniFile);
rewind(iniFile);
iniBuffer = (char*)malloc(sz + 1);
fread(iniBuffer, 1, sz, iniFile);
return iniBuffer;
}
// Given the path/name of an INI config file, return a map of its data
// which can be indexed by sections, and then by keys
INI_MAP make_ini_map(const char * configPath) {
FILE * configIni = fopen(configPath, "r");
fs::path configPathObj(configPath);
try {
if (!configIni) {
std::string fileError = "ERROR: Cannot open config file: ";
throw std::runtime_error(fileError);
}
} catch (std::runtime_error& e){
std::cerr << e.what() << configPathObj.filename() << std::endl;
return {};
}
INI_MAP iniMap;
char * iniBuffer = make_ini_buffer(configIni);
strip_ini_cache(iniBuffer, strlen(iniBuffer), INI_DEFAULT_FORMAT,
NULL, ini_callback, &iniMap);
delete [] iniBuffer;
return iniMap;
}
INI_MAP cfgIni = make_ini_map("../config.ini");
Note the final line, outside of any other function, defining 'cfgIni'. The idea is to define a global INI data storage object that can be used externally by other files -- for example, like this:
#include "ini_parse.h"
int main() {
extern INI_MAP cfgIni;
extern int size_path;
std::cout << cfgIni["General"]["output_directory"] << std::endl;
return 0;
}
Now for my problem: When declaring 'cfgIni' externally in this separate file, it appears that bits of memory are getting lost from where it is defined in my implementation file to where it is declared externally and caught here. I have no issue accessing the keys in the parent/child map, but the values are more illusive.
When printing the contents of the section "General", "output_directory" is returned, no problem.
I would expect the final line in the above code snippet to print the filepath char* array, to which "output_directory" is set in the INI file.
However, I instead get some random characters, like "�U". What is even more confusing is when I print out the size of this return value in memory using sizeof(), it returns '40', the correct number of characters in that filepath.
I have linked the documentation for the libraries I used and their functions. Apologies for the length/complexity of this question.
strip_ini_cache(): https://madmurphy.github.io/libconfini/html/confini_8c.html#a25d648d387f6e8bc36e7965accfca63b
Boost.variant: https://www.boost.org/doc/libs/1_61_0/doc/html/variant.html
Boost.filesystem: https://www.boost.org/doc/libs/1_80_0/libs/filesystem/doc/reference.html
Below is my header file:
#include <iostream>
#include <string>
#include <map>
#include <cstdio>
#include <confini.h>
#include <boost/filesystem.hpp>
#include <boost/variant.hpp>
#define BOOST_VAR boost::variant<int, bool, double, std::string, char*>
#define INI_MAP_ENTRY std::map<std::string, BOOST_VAR>
#define INI_MAP std::map<std::string, INI_MAP_ENTRY>
static int ini_callback(IniDispatch * const dispatch, void * map_pt);
char * make_ini_buffer(FILE * iniFile);
INI_MAP make_ini_map(const char * configPath);
The answer to this is really quite trivial. When passed by value, as I had done in one of my files, a shallow copy of the object was created, wherein the pointers to the char arrays were overwritten with wild addresses.
The solution was to pass by const reference.

How to assign char* variables dynamicly in C++?

I encounter a scenario to register arbitrary addresses as char* in a program.I need to pass each one as char* to the third party library for further action.
The third_party_function is describe as such in its header file:
virtual void RegisterFront(char *pszFrontAddress) = 0;
First, the program have to read from a config file with a group of addresses like:
tcp://18.19.20.22:7778; tcp://18.19.20.24:7778; tcp://18.19.20.25:7778;
The procedure to analyze this string and break it to a vector of addresses is simple if I define it as a cstring and use a loop. But then each has to be registered in that function in order for the library to connect to another address if current failed. Currently the configuration has 3 addresses but an arbitrary number of them should be allowed.
A static way to assign char* variables would be like this:
...
#include third_party_lib;
char* addr1 = "tcp://18.19.20.22:7778".c_str();
char* addr2 = "tcp://18.19.20.24:7778".c_str();
RegisterFront(addr1);
RegisterFront(addr2);
I omitted the loop to analyze the addresses out from the config, but the essence is clear: I cannot name addr[n]'s in the program since the number of addresses is uncertain - it depends on how many would be written in that config line.
i.e. For the 3 addresses scenario I can assign addr1 addr2 addr3 in the code. But once it is compiled, what if the config line now contains only two addresses? What about changing to 5?
How to improve the above to achieve dynamically assigning an arbitrary number of char* variables and pass each one of them to the third party function RegisterFront? - Of course you could replace it by some print function to try out, but the input type shall be the same.
BTW: Replacement for RegisterFront function:
void printFront(char *addr){
cout << addr <<endl;
}
Let's say your config line is this:
std::string s = "tcp://18.19.20.22:7778; tcp://18.19.20.24:7778; tcp://18.19.20.25:7778;"
s can also be a char*, it doesn't matter. At some point in your code, you've got a string representation of that config line that you've read from file or wherever.
Then to loop through that string for each URL and all your RegisterFront function is just this.
#include <iostream>
#include <vector>
#include <string>
#include <sstream>
void trim(std::string& s) {
size_t start = 0;
size_t end = s.size();
while ((start < end) && isspace(s[start]))
{
start++;
}
while ((end > start) && isspace(s[end - 1]))
{
end--;
}
s = s.substr(start, end - start);
}
void RegisterAddresses(const std::string& config_line) {
std::istringstream ss(config_line);
std::string address;
while (std::getline(ss, address, ';')) {
trim(address);
// address[0] returns a reference to the first char in the string
// hence, &address[0] is the char* to the front of the string
RegisterFront(&address[0]);
}
}
Then when you have your config_line (either defined as a char* or std::string instance), you just invoke it either way:
std::string s = "tcp://18.19.20.22:7778; tcp://18.19.20.24:7778; tcp://18.19.20.25:7778;";
RegisterAddresses(s);
This also works (a temporary std::string is passed to RegisterAddresses - constructed from the char* passed in)
const char* s = "tcp://18.19.20.22:7778; tcp://18.19.20.24:7778; tcp://18.19.20.25:7778;";
RegisterAddresses(s);

How to check char array contain any char without loop in C++?

I'm using the opendir and readdir functions to search for file names containing .txt in the given directory.
Is there any way I can test a certain extension via a function without using a loop? (currently I have to loop through de-> d_filename to check but they are quite complicated, in addition I tried de->d_type however it did not return the extension)
In addition, this function is returning the name of the file name, my desired result is to get the path name from the beginning, is there a function return wchar_t* similar to de->d_fullfilepath?
This is all I have :
DIR* dr = opendir(lpszFolder);
vector<const wchar_t*> names; //get list file with extension .txt then push to this vector
if (dr == NULL) // opendir returns NULL if couldn't open directory
{
printf("Could not open current directory");
return {};
}
// Refer http://pubs.opengroup.org/onlinepubs/7990989775/xsh/readdir.html
// for readdir()
while ((de = readdir(dr)) != NULL)
{
if (de->d_type ... 'txt') // function get just .txt file.
{
wchar_t* pwc =new wchar_t(lpszFolder); //initialize new instance file path
const size_t cSize = de->d_namlen + 1; //get file len
mbstowcs(pwc, de->d_name, cSize); //combine thisfilepath + extension
names.push_back(pwc);
}
}
Best Libc function to search reversely
You might consider strrchr
Locate last occurrence of character in string
Returns a pointer to the last occurrence of character in the C string str.
The terminating null-character is considered part of the C string. Therefore, it can also be located to retrieve a pointer to the end of a string.
Sample Program to find files with specific file extension
#include <string.h>
#include <sys/types.h>
#include <dirent.h>
#include <string>
#include <vector>
using namespace std;
const char *get_filename_ext(const char *filename) {
const char *dot = strrchr(filename, '.');
return (!dot || dot == filename) ? "" : dot + 1;
}
int main(int ac, char **av) {
if (ac != 2)
return 1;
const char *lookup = (ac==3) ? av[2] : "txt";
const char *lpszFolder = av[1];
DIR* dr = opendir(lpszFolder);
vector<const wchar_t*> names; //get list file with extension .txt then push to this vector
if (dr == NULL) // opendir returns NULL if couldn't open directory
{
printf("Could not open current directory");
return (1);
}
struct dirent *ent;
uint32_t len = sizeof(((dirent*)0)->d_name);
char ext[len];
while ((ent = readdir (dr)) != NULL) {
(void)ext;
strncpy(ext, get_filename_ext(ent->d_name), len-1);
if (!strcmp(lookup, ext))
names.push_back(reinterpret_cast < wchar_t*>(ent->d_name));
}
closedir(dr);
for (auto name : names)
printf("%s", (char *)name);
return 0;
}
Main Usage
Test with:
g++ a.cpp && ./a.out myfolder
will look for all files with ".txt" extensions
Or if you want a specific extension like ☠ :
g++ a.cpp && ./a.out myfolder ☠
In modern C++ you should use algorithms from the std::algorithm library to avoid loops. These algorithms prevent many possible problems from the wrong usage of loops, mostly out of bounds problems.
And, C++ can deal with "wide strings" with the base data type wchar_t. You can simply use std::wstring instead of std::string.
Any you should not and never use plain C-Style arrays or pointers to char or wchar_t. These are that error prone that they should really not be used.
Even if you have legacy code with "old" "char*"-strings, put them into a std::string and use those in the future.
Next: You MUST NOT use raw pointers for owned memory. You should try to avoid pointers in general and use smart pointers instead. And you should not use new in C++. There is nearly no need for it any longer. Use containers from the STL.
Now back to your original question:
How to check char array contain any char without loop in C++?
Yes, by using std::algorithmsand iterators
Is there any way I can test a certain extension via a function without using a loop?
Yes, ths std::filesystem will help you. It has all the functionality you need and is superior to all handcraftedt solutions. It can especially also deal with wchar_t and wide stringsstd::wstring
In the following code, I generated an example function that returns a std::vector filled with all fule file paths in a specified directory with a given string.
#include <iostream>
#include <string>
#include <filesystem>
#include <vector>
#include <algorithm>
// Name space alias for saving typing work
namespace fs = std::filesystem;
// A function, that gets a path to a director as wstring and returns all file paths as wstring with a given extension
std::vector<std::wstring> getFilesWithCertainExtension(const std::wstring& dirPath, const std::wstring& extension = L".txt") {
// Put the wstring with path to the the directory in a generic path variable
fs::path startPath{ dirPath };
// Here we sill store all directory entries having a given extension
std::vector<fs::directory_entry> filesInDirectory{};
// Go thorugh the directory and copy all directory entries with a given extension int our vector
std::copy_if(fs::directory_iterator(startPath), {}, std::back_inserter(filesInDirectory),
[&](const fs::directory_entry& de) { return de.path().extension().wstring() == extension; });
// The result of this function should be a vector of wstrings
std::vector<std::wstring> result(filesInDirectory.size());
// Convert directory entries to wstrings
std::transform(filesInDirectory.begin(), filesInDirectory.end(), result.begin(),
[](const fs::directory_entry& de) { return de.path().wstring(); });
return result;
}
int main() {
// Read all files from c:\\temp with the default extension ".txt"
std::vector<std::wstring> files = getFilesWithCertainExtension(L"c:\\temp");
// Show full paths to user
for (const std::wstring& ws : files) std::wcout << ws << L"\n";
return 0;
}
This is one of many possible solutions. This could even be optimized, if I would understand your requirements better.
I would explain the function in more detail. But, becuase anyway nobody will read this, I save the time.

Taking a pointer passed to a function, then putting it into another function

I'm writing a password generator that can read and write to a file. I have a function that takes an empty string, then modifies it. The function would look something like this:
void password_create(string *passwd)
Inside that function, I call a write function that writes out to a file with the password, it would look something like this:
void write_out(string file_name, string *passwd)
Then the total code looks like this:
void password_create(string *passwd) {
*passwd = "something";
write_out(&passwd);
}
The compile complains that I can't convert std::basic_string<char>** to std::basic_string<char>*.
I'm relatively new to C++, and this program is just to help me get acquainted with the language. I can pass passwd into the write_out() function without a * or & to denote a pointer or reference just fine. It won't give me an error if I type:
*passwd = "something";
write_out(passwd);
It doesn't affect the overall completion of the program, I was just curious as to why I get that error.
The variable passwd is already a pointer to a std::string, therefore taking the address of it via &passwd will give you something of type std::string** - so if write_out expects a parameter of type std::string* but receives the std::string** then the compiler will give you an error as you saw. So don't use & when passing passwd to write_out():
write_out(passwd);
But all that aside, you should just pass the std::string variables by reference rather than by pointer, as mentioned in the comments and the other answer.
Don't use pointers, in c++ prefer using pass by reference:
void password_create(std::string &passwd) {
passwrd = "something";
...
Then ensure that you create the string as you intend:
std::string myString;
password_create(myString);
That way you will have the memory you expect and you dont need to worry about pointer semantics.
No need to overcomplicate things. I dont see where pointers are needed in this case at all. Just make a function to generate a password and return it.
#include <string>
#include <fstream>
using namespace std;
string password_create() {
return "generated password";
}
void write_password_to_file(string file, string password) {
ofstream stream(file);
stream << password;
stream.close();
}
int main() {
auto password = password_create();
write_password_to_file("pathtofile.txt", password);
return 0;
}
As mentioned in the other answers you should actually take the std::string parameter by reference instead of a pointer.
Well assuming that the write_out() has some signature like
write_out(char* passwd);
or
write_out(const char* passwd);
you can pass std::string::operator[]():
void password_create(string *passwd) {
*passwd = "something";
write_out(&(*passwd)[0]);
}

C++ Set value for a char**

I'm trying to assign a value to a char** variable. In my foo.h I've defined a couple of variables such as
#define APIOCTET int
#define APILONG long
#define APICHAR char
#define APISTRING char*
Now in my foo.cpp I'm tryng to use a method where
APILONG apiInitialize(APISTRING filePath, APISTRING* outputString)
{
//open text file to where output will be printed
//do other stuff, etc..
//return result;
}
I'd like to assign a value to my APISTRING* outputString but I just can't figure out how to do so, I've tried many things which are basically a variation of the following code
APISTRING error = "error";
APISTRING other = "string";
APISTRING charArr[] = { error, other, error };
APISTRING *charArr2[] = { charArr };
errorString = *charArr2;
Also im not 100% clear on what exactly is APISTRING* outputString. When I try to compile it gives me an error message where it mentions it's a char**. Is it a 2D array?.. A pointer to an array of chars?.. But most importantly, how would I assign a value for this variable? Thanks in advance.
The APISTRING* is a pointer to a pointer to char. It holds an address which holds the address of the first character of the string in memory.
See this question for more info on double pointers in C/C++.
To assign to the string you would need to do *outputString = "string"
APISTRING* outputString will be pre-processed and replcaed at compile-time as char** outputstring. Hence, outputString will be double pointer hence, you need to do it like this (below code). I combined both .h and cpp together for simplicity.
#include<iostream>
using namespace std;
#define APIOCTET int
#define APILONG long
#define APICHAR char
#define APISTRING char*
APILONG apiInitialize(APISTRING filePath, APISTRING* outputString)
{
APISTRING getIt = *outputString;
cout<<" "<<getIt<<endl;
}
int main()
{
APISTRING str = "hello";
APISTRING* outputString = &str;
APILONG val = apiInitialize("world", outputString );
system("PAUSE");
return 0;
}
I would recommend to use std::string, it'll be easy to tweak with certain behaviors. Hope this helps.