Issue reading file with ifstream using absolute path - c++

Hello stack overflow community. I came here as a last resort because i probably made a stupid mistake i cannot see myself.
The question im asking is for some reason when i try to read a file with an absolute path(or relative, you can see i tried that in my code) it cannot read the file for some unknown reason(atleast to me). This is a small thing for a big project im working on. Thank you guys!
main.cpp
#include <iostream>
#include <fstream>
#include <filesystem>
#include <unistd.h>
#include <string>
std::string openf() {
FILE* pipe = popen("zenity --file-selection", "r"); // open a pipe with zenity
if (!pipe) return "ERROR"; // if failed then return "ERROR"
char buffer[912]; // buffer to hold data
std::string result = ""; // result that you add too
while(!feof(pipe)) { // while not EOF read
if(fgets(buffer, 912, pipe) != NULL) // get path and store it into buffer
result += buffer; // add buffer to result
}
//I thought i needed to convert the absolute path to relative but i did not after all
// char cwd[10000];
// getcwd(cwd, 10000); // get cwd(current working directory)
// result = std::filesystem::relative(result, cwd); // convert the absolute path to relative with cwd
pclose(pipe); // cleanup
return result;
}
std::string readf(std::string filename){
std::string res;
std::ifstream file;
file.open(filename.c_str());
if(file.is_open()) {
while(file){
res += file.get();
}
}else {
std::cout << "failed to open file " + filename;
}
return res;
}
int main( void ){
std::string file = openf();
std::cout << file << std::endl;
std::string str = readf(file);
std::cout << str << std::endl;
return 0;
}
output
/home/meepmorp/Code/Odin/test/test.odin
failed to open file /home/meepmorp/Code/Odin/test/test.odin

It seems zenity, which you use as file chooser, outputs an extra newline after the file name, which you include in the name. In Linux, files can actually contain embedded newline characters in their name, and you actually try to open "test.odin\n" instead of "test.odin".

Related

How to get fstream to save to any windows desktop that opens the .exe in c++

I'm making a program for my brother that will display 50,000 proxie variations and will save them all to a .txt.
How can I make it so any windows machine that uses this code will get the .txt to save to the desktop.
Here's what I have:
fstream file;
file.open("proxies.txt", ios::out);
string line;
streambuf* stream_buffer_cout = cout.rdbuf();
streambuf* stream_buffer_cin = cin.rdbuf();
streambuf* stream_buffer_file = file.rdbuf();
cout.rdbuf(stream_buffer_file);
for (int i = 1; i < 50001; i++)
{
cout << n1 << i << n2 << "\n";
}
file.close();
Thanks for any help.
If I get what you are asking you just need to replace "proxies.txt" with an absolute path to a file in the desktop folder. You can get the desktop directory with the Win32 call SHGetFolderPath and put the path together using the standard (C++17) file system calls if you want, as below:
#include <iostream>
#include <filesystem>
#include <fstream>
#include <shlobj_core.h>
namespace fs = std::filesystem;
std::string desktop_directory() {
char path[MAX_PATH + 1];
if (SHGetFolderPathA(HWND_DESKTOP, CSIDL_DESKTOP, NULL,
SHGFP_TYPE_DEFAULT, path) == S_OK) {
return path;
} else {
return {}; // I'm not sure why this would fail...
}
}
int main() {
std::fstream file;
auto desktop_path = fs::path(desktop_directory()) / "proxies.txt";
file.open(desktop_path, std::ios::out);
// ...
file.close();
return 0;
}

C++ vector change in text.txt file and add text

#include <iostream>
#include <string>
#include <stdio.h>
#include <cstring>
#include <fstream>
#include <vector>
#include <iterator>
using std::cout;
using std::cin;
using std::endl;
using std::string;
std::string FilesOpen(std::string command)
{
const int size_buffer = 2;
char buffer[size_buffer];
memset(buffer, 0, size_buffer * sizeof(char));
std::string result = "";
// Open pipe to file
FILE* pipe = popen(command.c_str(), "r");
if (!pipe)
{
return "popen failed!";
}
// read till end of process:
while (!feof(pipe))
{
// use buffer to read and add to result
if (fgets(buffer, 2, pipe) != NULL)
{
result += buffer;
memset(buffer, 0, size_buffer * sizeof(char));
}
}
pclose(pipe);
return result;
}
int main(int* agrc, char* agrv[])
{
std::vector<std::string> pole;
std::string text;
// get files names and use to ifstream files
FilesOpen("ls /root/workspace/src/server > /root/workspace/filestext.txt");
// get files info size and names
FilesOpen("ls -l /root/workspace/src/server > /root/workspace/filelist.txt");
// get files name and add vector
std::ifstream files;
files.open("/root/workspace/filestext.txt", std::ios_base::in);
if (!files)
{
std::cout << "Error not open files" << std::endl;
}
while (files >> text)
{
pole.push_back(text);
}
files.close();
for (std::vector<std::string>::iterator it = pole.begin(); it != pole.end(); ++it)
{
std::cout << *it << std::endl;
}
// replace text in shell
std::string filereplace = "/root/workspace/testovaci.sh";
std::ofstream r_file(filereplace.c_str());
char patch[] = "patch=";
if (r_file.is_open())
{
for (int i = 0; patch[i] != '\0'; i++)
r_file.put(patch[i]);
r_file.put('D');
}
r_file.close()
}
I need to get the contents of the file name from the filetext.txt file and ignore the folders and list them in the testovaci.sh script, which looks like this:
neco1
neco2
neco3
patch =
neco4
neco5
I need to put in the testovaci.sh file has been added to patch = "file". "file". "file"
and the folders were ignored, leaving only binary files.
Please help me, as I tried everything but nothing works.

Recieve 3 files by terminal and concatenate the content of file2 and file3 to file1 using fstream

I'm begginer in C++, so may be parts of my code doesn't have sense, sorry.
What I have to do is (C++, Linux, by fstream):
· Receive 3 or more files passed by terminal by:
./executable file1.txt file2.txt file3.txt
· programm a function that read the files file2.txt and file3.txt and copy it to file1.txt (concatenate, don't overwrite)
I don't know how to do it, I don't know anything about fstream, I'm just learning now by myself, so I really need help. Maybe there are similar questions solved in SO, but I don't know how to solve my problem by them.
I attach the code I have. I don't know how to code the function, so it's empty.
Thank you so much.
I try doing:
void concat(char *argv[], int numberoffilesreceived){
char c;
towritethefiles.open(argv[0], ios::app);
for(int i=1; i<numberoffilesreceived; i++){
toreadthefiles.open(argv[i], ios::in);
while(!toreadthefiles.eof()){
toreadthefiles >> c;
towritethefiles<< c;
}
}
}
It compiles but doesn't work, the program freezes when you run it.
and I also try using std::copy by I don't understand how it works.
ifstream toreadthefiles;
ofstream towritethefiles;
void concat(char *argv[], int numberoffilesreceived);
int main(int argc, char *argv[]){
/* 1/2 The code from below to 2/2 it's only to prevent path errors when receiving the files (it works fine) */
const char directory[SIZE]="./";
int count_files=0;
char files[SIZE][SIZE];
for(int i=1; i<argc; i++){
strcpy(files[i], directory);
strcat(files[i], argv[i]);
count_files++;
}
/*2/2 to add ./ to the name files when passed by terminal: ./executable ./file1.txt ./file2.txt ./file3.txt */
/*check if received almost 3 files like required */
if(argc<3){
cout<< "Error, to few files entered" << endl;
getchar();
exit(1);
}
/*pass the files to the concat function*/
for(int i=1; i<argc; i++){
concat(&argv[i], count_files);
}
toreadthefiles.close();
towritethefiles.close();
return 0;
}
void concat(char *argv[], int count_files){
}
I think I see an issue with your concat() function. You are calling concat() for each of the files passed in. Then in the function, you are using count_files to run that loop again for the number of files passed in.
I would consider rewriting concat() function so that it looks like this:
void concat(std::ofstream& outputStream, char* fileToAppend)
{
std::ifstream in(fileToAppend);
if (!in) {
cout << "Error, cannot open file: " << fileToAppend;
return;
}
// Use std::getline to read each line in the input stream,
// then write it to the output stream!
string line;
while (std::getline(in, line)) {
outputStream << line;
}
}
The benefit being that you can reuse the function for appending a single input file to an existing output stream, and you wrap up the check to ensure the file exists (you may want something more sophisticated like returning true/false on the file being appended, or throwing an error, etc.).
In main(), you would replace the code after the check for at least three files with something like:
// Create an output stream with the first file
// ios::out- output flag
// ios::app- append flag
std::ofstream out(argv[1], ios::out | ios::app);
// Make sure the file exists!
if (!out) {
cout << "Error, cannot open file: " << argv[1];
exit(1);
}
// For all other arguments, concat with the first.
for (int i = 2; i < argc; i++) {
concat(out, argv[i]);
}
You can use std::copy with stream iterators, and I've revised my previous suspicion that it would be slow, so here's one way using doing just that with comments in the code.
#include <iostream>
#include <fstream>
#include <vector>
#include <ios>
#include <stdexcept>
void concat(const std::string& destination, const std::vector<std::string>& sources) {
// open the destination file and keep it open until all is done
std::ofstream dest_fs(destination, std::ios_base::binary);
if(!dest_fs)
throw std::runtime_error("Could not write to \"" + destination + "\".");
// loop over the source files
for(const auto& source_file : sources) {
// open the current source file
std::ifstream source_fs(source_file, std::ios_base::binary);
if(!source_fs)
throw std::runtime_error("Could not read from \"" + source_file + "\".");
// copy from source to destination
std::copy(std::istreambuf_iterator<char>(source_fs),
std::istreambuf_iterator<char>(),
std::ostreambuf_iterator<char>(dest_fs));
}
}
int cppmain(std::string program, std::vector<std::string> args) {
if(args.size() < 2) {
std::cout << "USAGE: " << program << " destination_file input_file(s)\n";
return 1;
}
// extract the first argument which is the destination file
std::string destination_file = std::move(args.front());
args.erase(args.begin()); // erase first argument from the vector
try {
// do the concatenation
concat(destination_file, args);
return 0;
} catch(const std::exception& ex) {
std::cerr << program << ": ERROR: " << ex.what() << "\n";
return 1;
}
}
int main(int argc, char* argv[]) {
return cppmain(argv[0], {argv + 1, argv + argc});
}

Problems formatting arrays and strings in C++

I'm having a simple problem that's been driving me nuts all day. I am trying to open a file on the current user's Desktop without knowing the current user's name.
The idea is that I would use the GetCurrentUser call to the API to get the user name. Then format a string to give the full path directory, and pass that variable into fopen to open the file. Here is the code I'm working on, I get no compiler errors and it compiles fine but nothing writes to the file.
int main() {
char pathName[200]; // declaring arrays
char userName[100];
DWORD userNameSize = sizeof(userName); // storage for user name
if (!GetUserName(userName, &userNameSize)) { cout << "user not found"; }
else { cout "hello" << userName;} // error checking
// format for Windows 7 desktop
sprintf(pathName, "\"C:\\Users\\%s\\Desktop\\text.txt\"", userName);
cout << pathName << "\n"; // confirms correct location
const char* fileLocation = pathName; // pointer to full path to pass into fputs
const char* test = "test"; // test information to write to file to confirm
FILE *f = fopen(fileLocation,"a+"); // open file in append mode
fputs(test, f); // write to file
fclose(f); // flush and exit
return 0;
}
Maybe I need to use a different call to format the string? Or declare fileLocation as a different variable type?
I'm fairly new to C++ and would appreciate any tips that would help me to be able to open a file on the current user's Desktop. Thanks.
EDIT IN RESPONSE TO JERRY'S ADVICE:
This is what my latest comment was referring to:
#include <iostream>
#include <cstring>
#include <string>
#include <conio.h>
using namespace std;
string location ("C:\\Users\\testuser\\Desktop\\log.dat");
char cstr = char* [location.size()]; //This is a problematic line
strcpy (cstr, location.c_str());
void write(const char* c)
{
const char* fileLocation = cstr;
//const char* fileLocation = g_pathName;
FILE *f = fopen(fileLocation,"a+"); // This is the problematic line right here.
if(f!=NULL)
{
fputs(c,f); // append to end of file
fclose(f); // save so no entries are lost without being flushed
}
}
int main ()
{
write("test");
cout << "done";
_getch();
return 0;
}
You have a missing semicolon at line 9 where it says:
...{ cout << "user not found" }...
Semicolons are not optional in C++, you need them for a working program. Also, as stated in the comments, you do not need quotes around the name of the file.
I'd use SHGetSpecialFolderPath from shlobj.h:
const char *szFileName = "text.txt";
const char *szContent = "test string";
char szPath[_MAX_PATH];
SHGetSpecialFolderPath(NULL, szPath, CSIDL_DESKTOPDIRECTORY, FALSE);
strcat(szPath, "\\");
strcat(szPath, szFileName);
FILE *pFile = fopen(szPath, "a+");
if(pFile != NULL)
{
fputs(szContent, pFile);
fclose(pFile);
}
I would use SHGetKnownFolderPath with FOLDERID_Desktop to get the path to the desktop, then add a file name to the end. You also almost certainly want to do the manipulation on std::strings, then when you've created the full name, use the .c_str member function to retrieve the name as a C-style string. Unless you have a really specific reason to do otherwise, you're probably better off using a std::ofstream instead of a C-style FILE * as well (and in that case if your compiler is current, you can probably pass the std::string object directly as the name).
Edit: some quick demo code creating and writing to a file on the user's desktop:
#include <windows.h>
#include <Shlobj.h>
#include <objbase.h>
#include <string>
#include <fstream>
#pragma comment(lib, "ole32.lib")
#pragma comment(lib, "shell32.lib")
std::string GetKnownFolderPath(REFKNOWNFOLDERID f) {
PWSTR sys_path;
SHGetKnownFolderPath(f, 0, NULL, &sys_path);
DWORD size = WideCharToMultiByte(CP_ACP, 0, sys_path, -1, 0, 0, NULL, NULL);
std::string path(size, ' ');
WideCharToMultiByte(CP_ACP, 0, sys_path, -1, &path[0], size, NULL, NULL);
// We're finished with the string the system allocated:
CoTaskMemFree(sys_path);
// WideCharToMultiByte leaves space for a NUL terminator we don't need
path.resize(path.size()-1);
return path;
}
int main() {
std::string path(GetKnownFolderPath(FOLDERID_Desktop));
path += "\\test.txt";
std::ofstream test(path.c_str());
test << "This is a test";
return 0;
}

std::ofstream, check if file exists before writing

I am implementing file saving functionality within a Qt application using C++.
I am looking for a way to check to see if the selected file already exists before writing to it, so that I can prompt a warning to the user.
I am using an std::ofstream and I am not looking for a Boost solution.
This is one of my favorite tuck-away functions I keep on hand for multiple uses.
#include <sys/stat.h>
// Function: fileExists
/**
* Check if a file exists
*
* #param[in] filename - the name of the file to check
*
* #return true if the file exists, else false
*/
bool fileExists(const std::string& filename)
{
struct stat buf;
if (stat(filename.c_str(), &buf) != -1)
{
return true;
}
return false;
}
I find this much more tasteful than trying to open a file if you have no immediate intentions of using it for I/O.
bool fileExists(const char *fileName)
{
ifstream infile(fileName);
return infile.good();
}
This method is so far the shortest and most portable one. If the usage is not very sophisticated, this is one I would go for. If you also want to prompt a warning, I would do that in the main.
fstream file;
file.open("my_file.txt", ios_base::out | ios_base::in); // will not create file
if (file.is_open())
{
cout << "Warning, file already exists, proceed?";
if (no)
{
file.close();
// throw something
}
}
else
{
file.clear();
file.open("my_file.txt", ios_base::out); // will create if necessary
}
// do stuff with file
Note that in case of an existing file, this will open it in random-access mode. If you prefer, you can close it and reopen it in append mode or truncate mode.
With std::filesystem::exists of C++17:
#include <filesystem> // C++17
#include <iostream>
namespace fs = std::filesystem;
int main()
{
fs::path filePath("path/to/my/file.ext");
std::error_code ec; // For using the noexcept overload.
if (!fs::exists(filePath, ec) && !ec)
{
// Save to file, e.g. with std::ofstream file(filePath);
}
else
{
if (ec)
{
std::cerr << ec.message(); // Replace with your error handling.
}
else
{
std::cout << "File " << filePath << " does already exist.";
// Handle overwrite case.
}
}
}
See also std::error_code.
In case you want to check if the path you are writing to is actually a regular file, use std::filesystem::is_regular_file.
Try ::stat() (declared in <sys/stat.h>)
One of the way would be to do stat() and check on errno.
A sample code would look look this:
#include <sys/stat.h>
using namespace std;
// some lines of code...
int fileExist(const string &filePath) {
struct stat statBuff;
if (stat(filePath.c_str(), &statBuff) < 0) {
if (errno == ENOENT) return -ENOENT;
}
else
// do stuff with file
}
This works irrespective of the stream. If you still prefer to check using ofstream just check using is_open().
Example:
ofstream fp.open("<path-to-file>", ofstream::out);
if (!fp.is_open())
return false;
else
// do stuff with file
Hope this helps.
Thanks!