Comparing 2 strings to exclude based on extention - c++

Ive had a look at this post: Find if string ends with another string in C++
I am trying to achieve a similar goal.
Basically i want to take a file list from a directory and filter out any files which do not end with a specified allowed extention for processing in my program.
In java this would be performed by creating a method and passing the extention accross as a string then using .endswith in the following statement. C++ does not appear to support this so how would i go about it?
for (int fileList = 0; fileList < files.length; fileList++)
{
//output only jpg files, file list is still full
if(files[fileList].toString().endsWith(extension))
{
images.add(files[fileList]);
}//end if
}//end for
Thanks in advance

bool endsWith(std::string const & s, std::string const & e) {
if (s.size() < e.size())
return false;
return s.substr(s.size() - e.size()) == e;
}

If using boost::filesystem is ok for you then you could try
#include <boost/filesystem.hpp>
//...
boost::filesystem::path dir_path ("c:\\dir\\subdir\\data");
std::string extension(".jpg");
for (boost::filesystem::directory_iterator it_file(dir_path);
it_file != boost::filesystem::directory_iterator();
++it_file)
{
if ( boost::filesystem::is_regular_file(*it_file) &&
boost::filesystem::extension(*it_file) == extension)
{
// do your stuff
}
}
This will parse the given directory path and you then just have to filter desired extension.t

Next example checks if the filename ends with the jpg extension :
#include <iostream>
#include <string>
using namespace std;
bool EndsWithExtension (const string& str,const string &extension)
{
size_t found = str.find_last_of(".");
if ( string::npos != found )
{
return (extension == str.substr(found+1) );
}
return false;
}
int main ()
{
string filename1 ("c:\\windows\\winhelp.exe");
string filename2 ("c:\\windows\\other.jpg");
string filename3 ("c:\\windows\\winhelp.");
cout << boolalpha << EndsWithExtension(filename1,"jpg") << endl;
cout << boolalpha << EndsWithExtension(filename2,"jpg") << endl;
cout << boolalpha << EndsWithExtension(filename3,"jpg") << endl;
}

Related

How do I normalize a filepath in C++ using std::filesystem::path?

I am trying to convert a path string to a normalized (neat) format where any number of directory separators "\\" or "/" is converted to one default directory separator:
R"(C:\\temp\\Recordings/test)" -> R"(C:\temp\Recordings\test)"
Code:
#include <string>
#include <vector>
#include <iostream>
#include <filesystem>
std::string normalizePath(const std::string& messyPath) {
std::filesystem::path path(messyPath);
std::string npath = path.make_preferred().string();
return npath;
}
int main()
{
std::vector<std::string> messyPaths = { R"(C:\\temp\\Recordings/test)", R"(C://temp\\Recordings////test)" };
std::string desiredPath = R"(C:\temp\Recordings\test)";
for (auto messyPath : messyPaths) {
std::string normalizedPath = normalizePath(messyPath);
if (normalizedPath != desiredPath) {
std::cout << "normalizedPath: " << normalizedPath << " != " << desiredPath << std::endl;
}
}
std::cout << "Press any key to continue.\n";
int k;
std::cin >> k;
}
Output on Windows VS2019 x64:
normalizedPath: C:\\temp\\Recordings\test != C:\temp\Recordings\test
normalizedPath: C:\\temp\\Recordings\\\\test != C:\temp\Recordings\test
Reading the std::filepath documentation:
A path can be normalized by following this algorithm:
1. If the path is empty, stop (normal form of an empty path is an empty path)
2. Replace each directory-separator (which may consist of multiple slashes) with a single path::preferred_separator.
...
Great, but which library function does this? I do not want to code this myself.
As answered by bolov:
std::string normalizePath(const std::string& messyPath) {
std::filesystem::path path(messyPath);
std::filesystem::path canonicalPath = std::filesystem::weakly_canonical(path);
std::string npath = canonicalPath.make_preferred().string();
return npath;
}
weakly_canonical does not throw an exception if path does not exist.
canonical does.

Difficulties with string declaration/reference parameters (c++)

Last week I got an homework to write a function: the function gets a string and a char value and should divide the string in two parts, before and after the first occurrence of the existing char.
The code worked but my teacher told me to do it again, because it is not well written code. But I don't understand how to make it better. I understand so far that defining two strings with white spaces is not good, but i get out of bounds exceptions otherwise. Since the string input changes, the string size changes everytime.
#include <iostream>
#include <string>
using namespace std;
void divide(char search, string text, string& first_part, string& sec_part)
{
bool firstc = true;
int counter = 0;
for (int i = 0; i < text.size(); i++) {
if (text.at(i) != search && firstc) {
first_part.at(i) = text.at(i);
}
else if (text.at(i) == search&& firstc == true) {
firstc = false;
sec_part.at(counter) = text.at(i);
}
else {
sec_part.at(counter) = text.at(i);
counter++;
}
}
}
int main() {
string text;
string part1=" ";
string part2=" ";
char search_char;
cout << "Please enter text? ";
getline(cin, text);
cout << "Please enter a char: ? ";
cin >> search_char;
divide(search_char,text,aprt1,part2);
cout << "First string: " << part1 <<endl;
cout << "Second string: " << part2 << endl;
system("PAUSE");
return 0;
}
I would suggest you, learn to use c++ standard functions. there are plenty utility function that can help you in programming.
void divide(const std::string& text, char search, std::string& first_part, std::string& sec_part)
{
std::string::const_iterator pos = std::find(text.begin(), text.end(), search);
first_part.append(text, 0, pos - text.begin());
sec_part.append(text, pos - text.begin());
}
int main()
{
std::string text = "thisisfirst";
char search = 'f';
std::string first;
std::string second;
divide(text, search, first, second);
}
Here I used std::find that you can read about it from here and also Iterators.
You have some other mistakes. you are passing your text by value that will do a copy every time you call your function. pass it by reference but qualify it with const that will indicate it is an input parameter not an output.
Why is your teacher right ?
The fact that you need to initialize your destination strings with empty space is terrible:
If the input string is longer, you'll get out of bound errors.
If it's shorter, you got wrong answer, because in IT and programming, "It works " is not the same as "It works".
In addition, your code does not fit the specifications. It should work all the time, independently of the current value which is stored in your output strings.
Alternative 1: your code but working
Just clear the destination strings at the beginning. Then iterate as you did, but use += or push_back() to add chars at the end of the string.
void divide(char search, string text, string& first_part, string& sec_part)
{
bool firstc = true;
first_part.clear(); // make destinations strings empty
sec_part.clear();
for (int i = 0; i < text.size(); i++) {
char c = text.at(i);
if (firstc && c != search) {
first_part += c;
}
else if (firstc && c == search) {
firstc = false;
sec_part += c;
}
else {
sec_part += c;
}
}
}
I used a temporary c instead of text.at(i) or text\[i\], in order to avoid multiple indexing But this is not really required: nowadays, optimizing compilers should produce equivalent code, whatever variant you use here.
Alternative 2: use string member functions
This alternative uses the find() function, and then constructs a string from the start until that position, and another from that position. There is a special case when the character was not found.
void divide(char search, string text, string& first_part, string& sec_part)
{
auto pos = text.find(search);
first_part = string(text, 0, pos);
if (pos== string::npos)
sec_part.clear();
else sec_part = string(text, pos, string::npos);
}
As you understand yourself these declarations
string part1=" ";
string part2=" ";
do not make sense because the entered string in the object text can essentially exceed the both initialized strings. In this case using the string method at can result in throwing an exception or the strings will have trailing spaces.
From the description of the assignment it is not clear whether the searched character should be included in one of the strings. You suppose that the character should be included in the second string.
Take into account that the parameter text should be declared as a constant reference.
Also instead of using loops it is better to use methods of the class std::string such as for example find.
The function can look the following way
#include <iostream>
#include <string>
void divide(const std::string &text, char search, std::string &first_part, std::string &sec_part)
{
std::string::size_type pos = text.find(search);
first_part = text.substr(0, pos);
if (pos == std::string::npos)
{
sec_part.clear();
}
else
{
sec_part = text.substr(pos);
}
}
int main()
{
std::string text("Hello World");
std::string first_part;
std::string sec_part;
divide(text, ' ', first_part, sec_part);
std::cout << "\"" << text << "\"\n";
std::cout << "\"" << first_part << "\"\n";
std::cout << "\"" << sec_part << "\"\n";
}
The program output is
"Hello World"
"Hello"
" World"
As you can see the separating character is included in the second string though I think that maybe it would be better to exclude it from the both strings.
An alternative and in my opinion more clear approach can look the following way
#include <iostream>
#include <string>
#include <utility>
std::pair<std::string, std::string> divide(const std::string &s, char c)
{
std::string::size_type pos = s.find(c);
return { s.substr(0, pos), pos == std::string::npos ? "" : s.substr(pos) };
}
int main()
{
std::string text("Hello World");
auto p = divide(text, ' ');
std::cout << "\"" << text << "\"\n";
std::cout << "\"" << p.first << "\"\n";
std::cout << "\"" << p.second << "\"\n";
}
Your code will only work as long the character is found within part1.length(). You need something similar to this:
void string_split_once(const char s, const string & text, string & first, string & second) {
first.clear();
second.clear();
std::size_t pos = str.find(s);
if (pos != string::npos) {
first = text.substr(0, pos);
second = text.substr(pos);
}
}
The biggest problem I see is that you are using at where you should be using push_back. See std::basic_string::push_back. at is designed to access an existing character to read or modify it. push_back appends a new character to the string.
divide could look like this :
void divide(char search, string text, string& first_part,
string& sec_part)
{
bool firstc = true;
for (int i = 0; i < text.size(); i++) {
if (text.at(i) != search && firstc) {
first_part.push_back(text.at(i));
}
else if (text.at(i) == search&& firstc == true) {
firstc = false;
sec_part.push_back(text.at(i));
}
else {
sec_part.push_back(text.at(i));
}
}
}
Since you aren't handling exceptions, consider using text[i] rather than text.at(i).

Program To Identify Lower Case Strings

Beginner C++ student here, first ever programming class. I am trying to put together a program that will identify if a string is all lower case or not. I got as far as the code below. However, I need to account for spaces " ". If there is a space in the string that is input by the user, the program is suppose to return that it is not all lower case. Example:
input: abc def
return: The string is not lower case.
Would any of you ever so kindly advise what would be the best way to account for this in the code below?
NOTE: I know I have 'included' some extra header files, but that is because this is going to be part of another program and this is just an excerpt to get things running.
Thank you so very much all!!
#include <fstream>
#include <iostream>
#include <string>
#include <cstdlib>
#include <algorithm>
#include <cctype>
#include <iomanip>
using namespace std;
bool die(const string & msg);
bool allLower(const string & l);
int main() {
string l;
cout << "\nEnter a string (all lower case?): ";
cin >> l;
if (allLower(l) == true)
{
cout << "The string is lower case." << endl;
}
else
{
cout << "The string is not lower case." << endl;
}
}
bool allLower(const string & l) {
struct IsUpper {
bool operator()(int value) {
return ::isupper((unsigned char)value);
}
};
return std::find_if(l.begin(), l.end(), IsUpper()) == l.end();
}
bool die(const string & msg){
cout << "Fatal error: " << msg << endl;
exit(EXIT_FAILURE);
}
You could use a good old fashion for-loop.
bool allLower(const std::string & l)
{
for(unsigned int i = 0; i < l.size(); i++)
{
if(l[i] == ' ')
{
return false;
}
else if(isalpha(l[i]))
{
if(isupper(l[i]))
return false;
}
}
return true;
}
Note that if you feed it in something like "2" it will return true. You could add a final else statement that returns false if you so desire.
You can check to see if a character is alphabetic using the function std::isalpha() prior to using std::isupper() or std::islower() to checking whether all letters within your string are uppercase/lowercase, etc
A range-based for loop would be clearer than indices IMO
bool allLower(const std::string &l)
{
for (auto c : l)
{
if ((c == ' ') ||
(std::isalpha(c) && std::isupper(c)))
{
return false;
}
}
return true;
}

How to check if a text file only contains certain characters (eg. 's','f','#','\n')(update)

I am doing a maze program and read the maze from a file, but i need to check the file only contain "s","f",“#” or "\n" characters otherwise print error messages? i tried many times , but really confused it ~
Now i'm trying to use STL to solve it, but have new problems!
void fillList(list<char> &myList, const char *mazeFile )
{
ifstream inFile;
string lines;
inFile.open(mazeFile);
while(!inFile.eof())
{
getline(inFile,lines);
for(int i=0;i<lines.length();i++)
myList.push_back(lines[i]);
}
}
bool checkMaze(list<char> &myList)
{
list<char>::iterator itr;
for (itr = myList.begin(); itr != myList.end(); itr++ )
{
if(*itr != 's' || *itr != 'f' || *itr != '#' || *itr != '\n')
return false;
}
return true;
}
myMaze.fillList(myList,argv[1]);
bool valid = myMaze.checkMaze(myList);
if(myMaze.isValid(argv[1]) && valid == true)
myMaze.printMaze();
else
{
cout << "Unable to load maze " << argv[1] << "\n";
return 0;
}
But it still not print ? what problem with that ?
Since you have a std::string, you might consider the various string search member functions.
E.g. string::find_first_not_of
std::string str ("s###X##f");
std::size_t found = str.find_first_not_of("sf#\n");
if (found!=std::string::npos) {
std::cout << "The first non-acceptible character is " << str[found];
std::cout << " at position " << found << '\n';
}
Here's a method that checks if a file contains a specific string:
#include <iostream>
#include <fstream>
#include <string>
using namespace std;
bool wordExists(char* file, char* word)
{
string line;
ifstream fileStream;
fileStream.open(file);
//until we can't read any more lines
while( getline(fileStream, line) )
{
if ( line.find(word) != string::npos )
return true;
}
return false;
}
Since you haven't provided your implementation of the problem, I can't determine what your error was - but feel free to let me know if this code doesn't work (I just roughed it out in Notepad++ for this question) or if you have any additional concerns.

How to read in one file and display content vertically and then read in another file and display content vertically in c++

Hi I'm working on a program that reads in two files and i want to display the files content in their own columns for ex.
File1 File2
Time data Time data
I'm not quit sure how to create columns as such, I already have the code to read in the files and perform the functions needed its the output I'm stumped on. If anyone has any suggestions or help that would be awesome. Thanks! PS. This is NOT Homework Related.
I would do something like this:
#include <iostream>
#include <fstream>
#include <iomanip>
#include <algorithm>
#include <string>
using namespace std;
int readLines(const std::string& fileName1, const std::string& fileName2)
{
string line1;
string line2;
ifstream file1 (fileName1.c_str());
ifstream file2 (fileName2.c_str());
if (file1.is_open() && file2.is_open())
{
cout << setw(20) << left << "File1" << "File2" << endl;
bool done;
done = file1.eof() && file2.eof();
while (!done)
{
getline (file1, line1);
getline (file2, line2);
line1.erase(std::remove(line1.begin(), line1.end(), '\n'), line1.end());
line2.erase(std::remove(line2.begin(), line2.end(), '\n'), line2.end());
cout << setw(20) << left << (file1.eof() ? "" : line1) << (file2.eof() ? "" : line2) << endl;
done = file1.eof() && file2.eof();
}
file1.close();
file2.close();
}
else
{
cout << "Unable to open some file";
}
return 0;
}
int main ()
{
std::string fileName1("example1.txt");
std::string fileName2("example2.txt");
readLines(fileName1, fileName2);
return 0;
}
It really depends on what tools you are planning to use...
You can use some version of "curses" (a library with console manipulation functions such as "go to this position", "print text in green", etc), and then just walk around the screen as you like.
Or you could just read the files into separate variables and then print from each file in a loop. This requires no special codding. Just use an array or vector for the files themselves and the data you read from them.
Something like this:
const int nfiles = 2;
const char *filenames[nfiles] = { "file1.txt", "file2.txt" };
ifstream files[nfiles];
for(int i = 0; i < nfiles; i++)
{
if (!files[i].open(filenames[i]))
{
cerr << "Couldn't open file " << filenames[i] << endl;
exit(1);
}
}
bool done = false;
while(!done)
{
int errs = 0;
std::string data[nfiles];
for(int i = i < nfiles; i++)
{
if (!(files[i] >> data[i]))
{
errs++;
data[i] = "No data";
}
}
if (errs == nfiles)
{
done = true;
}
else
{
for(int i = 0; i < nfiles; i++)
{
... display data here ...
}
}
}