I have to write a program that will go through a given folder and use regex_search to find every instance of a certain string. I've got the regex_search working on it's own now, and I'm just trying to figure out how to go through each file. I want to attempt it using directory but am unsure where I would put it. Would I have to put the search through the file into my main method or would I have to create a seperate function outside of the main method for going through each file and call on it within the main method?
This is what I have now. Any tips you guys can give on how to approach this would be greatly appreciated!
Right now the function of it is to read an input text file and output a txt file that shows all the instances and the line number of each apperance. I am not required to see which lines they are on, use a particular file, or make an output file for this program, what I find will simply be printed to the console. I've left what I have now because I'm not sure if I'll checking each indivdual file in a similar fashion just with a different cariable name.
#include <iostream>
#include <regex>
#include <string>
#include <fstream>
#include <vector>
#include <regex>
#include <iomanip>
using namespace std;
int main (int argc, char* argv[]){
// validate the command line info
if( argc < 2 ) {
cout << "Error: Incorrect number of command line arguments\n"
"Usage: grep\n";
return EXIT_FAILURE;
}
//Declare the arguments of the array
string resultSwitch = argv[1];
string stringToGrep = argv[2];
string folderName = argv [3];
regex reg(stringToGrep);
// Validate that the file is there and open it
ifstream infile( inputFileName );
if( !infile ) {
cout << "Error: failed to open <" << inputFileName << ">\n"
"Check filename, path, or it doesn't exist.\n";
return EXIT_FAILURE;
}
while(getline(infile,currentLine))
{
lines.push_back( currentLine );
currentLineNum++;
if( regex_search( currentLine, reg ) )
outFile << "Line " << currentLineNum << ": " << currentLine << endl;
}
infile.close();
}
Reading a directory/folder is operating system dependent. In a UNIX/Linux/MacOS world, you use opendir(), and readdir():
#include <sys/types.h>
#include <dirent.h>
...
DIR *directory = opendir( directoryName );
if( directory == NULL )
{
perror( directoryName );
exit( -2 );
}
// Read the directory, and pull in every file that doesn't start with '.'
struct dirent *entry;
while( NULL != ( entry = readdir(directory) ) )
{
// by convention, UNIX files beginning with '.' are invisible.
// and . and .. are special anyway.
if( entry->d_name[0] != '.' )
{
// you now have a filename in entry->d_name;
// do something with it.
}
}
Related
I'm trying to make a small program that will create a Batch file, do something in it and then return a string from it, and after it delete the Batch.
I would like to store the output of the batch file in the variable line.
I tried using getline() but I think it work with .txt files only. I can be wrong.
#include <iostream>
#include <fstream>
#include <stdlib.h>
#include <string>
using namespace std;
int main(int argc, char *argv[]) {
ofstream batch;
string line;
batch.open("temp.bat", ios::out);
batch <<"#echo OFF\nwmic os get caption /value\nwmic path win32_videocontroller get description /value\npause\nexit";
batch.close();
system("temp.bat");
remove("temp.bat");
};
In my code I simply using system with my Batch file. I'd like to use cout<<line.
I expect string called line would be equal to the output of the Batch file.
You need to redirect output when using system():
#include <cstdio> // std::remove(const char*)
#include <cstdlib> // std::system(const char*)
#include <fstream>
#include <iostream>
#include <string>
#include <unordered_map>
int main()
{
std::string foo_bat = "foo.bat";
std::string foo_out = "foo.out";
// Write the batch file
{
std::ofstream f( foo_bat );
f << R"z(
#echo off
wmic os get caption /value
wmic path win32_videocontroller get description /value
)z";
}
// Execute the batch file, redirecting output using the current (narrow) code page
if (!!std::system( (foo_bat + " | find /v \"\" > " + foo_out + " 2> NUL").c_str() ))
{
// (Clean up and complain)
std::remove( foo_bat.c_str() );
std::remove( foo_out.c_str() );
std::cout << "fooey!\n";
return 1;
}
// Read the redirected output file
std::unordered_map <std::string, std::string> env;
{
std::ifstream f( foo_out );
std::string s;
while (getline( f >> std::ws, s ))
{
auto n = s.find( '=' );
if (n != s.npos)
env[ s.substr( 0, n ) ] = s.substr( n+1 );
}
}
// Clean up
std::remove( foo_bat.c_str() );
std::remove( foo_out.c_str() );
// Show the user what we got
for (auto p : env)
std::cout << p.first << " : " << p.second << "\n";
}
WMIC is a problematic program when it comes to controlling the output code page, hence the weird pipe trick we use with system().
But, after all that, you should use the WMI API directly to get this kind of information.
A possible, though admittedly not ideal solution would be to have the batch file write it's output to a .txt file and then read in that file into your program. Look at this SO thread to see how to do this.
This question already has answers here:
How do you iterate through every file/directory recursively in standard C++?
(19 answers)
Closed 8 years ago.
I know the number of files in my \data directory (n). I want to do something like that:
#include <string>
#include <fstream>
ifstream myFile;
string filename;
for(int i=0;i<n;i++)
{
filename=//call i'th file from the \data directory
myFile.open(filename);
//do stuff
myFile.close();
}
How can I do that?
Handling directories is not part of the C++ standard library. You can use platform dependent APIs (e.g. dirent.h on POSIX) or a wrapper around them, e.g. boost::filesystem.
If you use a do-while like I did here, you find the first file with FindFirstFile then read through them until you run out of .txt files. I'm not sure that do-while is necessarily the most effective method however.
#include <windows.h>
#include <iostream>
#include <fstream>
#include <string>
#include <cstdlib>
using namespace std;
int main()
{
string path = "c:\\data\\";
string searchPattern = "*.txt";
string fullSearchPath = path + searchPattern;
WIN32_FIND_DATA FindData;
HANDLE hFind;
hFind = FindFirstFile( fullSearchPath.c_str(), &FindData );
if( hFind == INVALID_HANDLE_VALUE )
{
cout << "Error searching data directory\n";
return -1;
}
do
{
string filePath = path + FindData.cFileName;
ifstream in( filePath.c_str() );
if( in )
{
// do stuff
}
else
{
cout << "Problem opening file from data" << FindData.cFileName << "\n";
}
}
while( FindNextFile(hFind, &FindData) > 0 );
if( GetLastError() != ERROR_NO_MORE_FILES )
{
cout << "Something went wrong during searching\n";
}
system("pause");
return 0;
}
`
I am writing a simple program to take in two files. The terminal command line looks like this.
./fileIO foo.code foo.encode
When it runs, the second file is not read in. When I enter
./fileIO foo.code foo.code
it works. I can't seem to figure out why the second one is not opening. Any ideas? Thanks!
#include <fstream>
#include <iostream>
#include <queue>
#include <iomanip>
#include <map>
#include <string>
#include <cassert>
using namespace std;
int main( int argc, char *argv[] )
{
// convert the C-style command line parameter to a C++-style string,
// so that we can do concatenation on it
assert( argc == 3 );
const string code = argv[1];
const string encode = argv[2];
string firstTextFile = code;
string secondTextFile = encode;
//manipulate the first infile
ifstream firstFile( firstTextFile.c_str(), ios::in );
if( !firstFile )
{
cerr << "Cannot open text file for input" << endl;
return 1;
}
string lineIn;
string codeSubstring;
string hexSubstring;
while( getline( firstFile, lineIn ) )
{
hexSubstring = lineIn.substr(0, 2);
codeSubstring = lineIn.substr(4, lineIn.length() );
cout << hexSubstring << ", " << codeSubstring << endl;
}
//manipulate the second infile
ifstream secondFile( secondTextFile.c_str(), ios::in );
if( !secondFile )
{
cerr << "Cannot open text file for input" << endl;
return 1;
}
char characterIn;
while( secondFile.get( characterIn ) )
{
cout << characterIn << endl;
}
return 0;
}
One thing you might want to try is adding the close() call as is standard procedure after you're done using files. Sometimes issues arise with re-opening files if they were not closed properly in a previous run.
firstFile.close();
secondFile.close();
Also, you may try restarting the computer if there is some lingering file handle that hasn't been released.
Hey everyone. I need to write a POSIX program to search through an entire file system for a specified file starting at the top directory. I've got some code which isn't done at all, but when I run it, and check to see if a particular file is a directory, it's saying this file which is not at all a directory is a directory and is trying to move into it, causing an error. I'm not sure how I can tell it that this type of file isn't a directory.
Here's my code. I know it's not perfect and I could probably do some things differently in the way of getting the directory names and passing them into the function. Either way, I'm pretty sure I have to do this recursively.
The file in question is /dev/dri/card0 and I'm running this from a Debian virtual machine.
#include <sys/types.h>
#include <sys/stat.h>
#include <dirent.h>
#include <unistd.h>
#include <time.h>
#include <stdint.h>
#include <locale.h>
#include <langinfo.h>
#include <fcntl.h>
#include <iostream>
#include <stdio.h>
#include <string>
using namespace std;
void SearchDirectory(string file_Name, string directory){
string new_Directory = directory;
DIR *dirp;
dirp = opendir(directory.c_str());
struct dirent *dptr;
struct stat statStruct;
while(dptr = readdir(dirp)){
stat(dptr->d_name, &statStruct);
if( S_ISDIR(statStruct.st_mode) ){
string check = dptr->d_name;
if ( check.compare(".") == 0 || check.compare("..") == 0 ){
continue;
}
else{
cout << dptr->d_name << " is is a directory" << endl;
new_Directory.append("/");
new_Directory.append(dptr->d_name);
SearchDirectory(file_Name, new_Directory);
}
}
else if( S_ISREG(statStruct.st_mode)){
string check = dptr->d_name;
if( check.compare(file_Name) == 0){
cout << "Found " << file_Name << " in " << directory << "/" << endl;
}
}
}
}
int main(int argc, char *argv[]){
if(argc < 2 || argc > 2){
cerr << "This program will find the specified file." << endl;
cerr << "Usage: mysearch <filename>" << endl;
return 1;
}
string file_Name = argv[1];
SearchDirectory(file_Name, "/");
return 0;
}
POSIX.2 requires a working "find" command.
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
int main(int argc, char **argv)
{
if (argc != 2) {
fprintf(stderr, "Usage: %s <filename>", argv[0]);
}
execlp("find", "find", "/", "-name", argv[1], "-print", (char *)NULL);
exit(EXIT_FAILURE);
}
->d_name returns just the name of the file, not the path to the file. You need to stat (not yet constructed) new_Directory instead of dptr->d_name.
You also have a problem if a directory contains more than one subdirectories. Your construction of new_Directory is incorrect for each subdirectory after the first.
You never closedir your directory handle, so you run out of resources. You should also consider loading the entire directory into an array before recursing to avoid running out of handles.
void SearchDirectory(string directory, string target_File_Name){
DIR *dirp = opendir(directory.c_str());
if (!dirp) {
perror(("opendir " + directory).c_str());
return;
}
struct dirent *dptr;
while(dptr = readdir(dirp)){
string file_Name = dptr->d_name;
string file_Path = directory + "/" + file_Name;
struct stat statStruct;
stat(file_Path.c_str(), &statStruct);
if( S_ISDIR(statStruct.st_mode) ){
if ( file_Name.compare(".") == 0 || file_Name.compare("..") == 0 ){
continue;
}
SearchDirectory(file_Path, target_File_Name);
}
else if( S_ISREG(statStruct.st_mode)){
if( file_Name.compare(target_File_Name) == 0){
cout << file_Path << endl;
}
}
}
closedir(dirp);
}
Update: Added second problem.
Update: Added third problem.
Update: Added code.
Not for the benefit of the OP, who writes "The point is to come up with a way to do it myself," but rather for the benefit of posterity, here is a way to use Boost.Filesystem:
#include <boost/filesystem.hpp>
namespace fs = boost::filesystem;
// sample usage: find_file("/home", ".profile");
void find_file( const fs::path& dirPath, const std::string& fileName) {
fs::recursive_directory_iterator end;
for(fs::recursive_directory_iterator it(dirPath); it != end; ++it) {
if(it->leaf() == fileName)
std::cout << it->path() << "\n";
if(fs::is_symlink(it->symlink_status()))
it.no_push();
}
}
Use fork, execv and the Unix implemented /usr/bin/find process and redirect its output for your result area?
I'm not sure if it's POSIX or not but the nftw library function is widely available on UNIX (HP-UX, AIX, Linux).
Your problem is "search a tree for a match"
BFS and DFS are the canonical basic algorithms. Give them a start node and go.
You will get into trouble if you follow symlinks; so test for them and don't follow them.
You should be able to map each point in the *FS algorithms to a directory operation.
Since C++ is an option, why not use something like Boost.Filesystem? The Boost.Filesystem two-minute tutorial gives an example of how to implement your search using directory iterators.
I've got a C++ program that uses SQLite. I want to store the SQL queries in a separate file -- a plain-text file, not a source code file -- but embed that file in the executable file like a resource.
(This has to run on Linux, so I can't store it as an actual resource as far as I know, though that would be perfect if it were for Windows.)
Is there any simple way to do it, or will it effectively require me to write my own resource system for Linux? (Easily possible, but it would take a lot longer.)
You can use objcopy to bind the contents of the file to a symbol your program can use. See, for instance, here for more information.
Use macros. Technically that file would be source code file but it wouldn't look like this.
Example:
//queries.incl - SQL queries
Q(SELECT * FROM Users)
Q(INSERT [a] INTO Accounts)
//source.cpp
#define Q(query) #query,
char * queries[] = {
#include "queries.incl"
};
#undef Q
Later on you could do all sorts of other processing on that file by the same file, say you'd want to have array and a hash map of them, you could redefine Q to do another job and be done with it.
You can always write a small program or script to convert your text file into a header file and run it as part of your build process.
Here's a sample that we used for cross-platform embeddeding of files.
It's pretty simplistic, but will probably work for you.
You may also need to change how it's handling linefeeds in the escapeLine function.
#include <string>
#include <iostream>
#include <fstream>
#include <cstdio>
using namespace std;
std::string escapeLine( std::string orig )
{
string retme;
for (unsigned int i=0; i<orig.size(); i++)
{
switch (orig[i])
{
case '\\':
retme += "\\\\";
break;
case '"':
retme += "\\\"";
break;
case '\n': // Strip out the final linefeed.
break;
default:
retme += orig[i];
}
}
retme += "\\n"; // Add an escaped linefeed to the escaped string.
return retme;
}
int main( int argc, char ** argv )
{
string filenamein, filenameout;
if ( argc > 1 )
filenamein = argv[ 1 ];
else
{
// Not enough arguments
fprintf( stderr, "Usage: %s <file to convert.mel> [ <output file name.mel> ]\n", argv[0] );
exit( -1 );
}
if ( argc > 2 )
filenameout = argv[ 2 ];
else
{
string new_ending = "_mel.h";
filenameout = filenamein;
std::string::size_type pos;
pos = filenameout.find( ".mel" );
if (pos == std::string::npos)
filenameout += new_ending;
else
filenameout.replace( pos, new_ending.size(), new_ending );
}
printf( "Converting \"%s\" to \"%s\"\n", filenamein.c_str(), filenameout.c_str() );
ifstream filein( filenamein.c_str(), ios::in );
ofstream fileout( filenameout.c_str(), ios::out );
if (!filein.good())
{
fprintf( stderr, "Unable to open input file %s\n", filenamein.c_str() );
exit( -2 );
}
if (!fileout.good())
{
fprintf( stderr, "Unable to open output file %s\n", filenameout.c_str() );
exit( -3 );
}
// Write the file.
fileout << "tempstr = ";
while( filein.good() )
{
string buff;
if ( getline( filein, buff ) )
{
fileout << "\"" << escapeLine( buff ) << "\"" << endl;
}
}
fileout << ";" << endl;
filein.close();
fileout.close();
return 0;
}
It's slightly ugly, but you can always use something like:
const char *query_foo =
#include "query_foo.txt"
const char *query_bar =
#include "query_bar.txt"
Where query_foo.txt would contain the quoted query text.
I have seen this to be done by converting the resource file to a C source file with only one char array defined containing the content of resource file in a hexadecimal format (to avoid problems with malicious characters). This automatically generated source file is then simply compiled and linked to the project.
It should be pretty easy to implement the convertor to dump C file for each resource file also as to write some facade functions for accessing the resources.