With C++17 (or Boost::filesystem), we can get the current path / current working directory using filesystem::current_path(). However - that gives us an absolute path.
We could also use an empty path as the relative current path - sometimes.
But - is it possible to obtain, portably, the equivalent of "." or "./" ? i.e. a non-empty relative current path?
Use "." for the current directory.
std::filesystem will recognize "." as representing the current directory / path - regardless of the platform you're on. So, it will not just happen to work on Linux/Windows, it is guaranteed to work.
auto relative_current path = std::filesystem::path{"."};
Relevant wording in the standard: fs.path.generic.3.
This answer is basically due to #NathanOliver...
Related
I just noticed that std::filesystem::recursive_directory_iterator uses different path separateors (i.e. / vs \) depending on whether it's on Windows or Linux, is there a way to make it return paths with '/' to make it consistent across systems?
This is how I am getting the paths:
for(auto& path: fs::recursive_directory_iterator(dir_path))
{
// Skip directories in the enumeration.
if(fs::is_directory(path)) continue;
string path_str = path.path().string();
}
What I mean is, the contents of path_str will be different between the two OSs (because the separators will be different), I would like them to be the same. I could just replace them on the final string, but that uses more cycles than if I can instruct the stl to use '/' for everything isntead.
So, your problem has nothing to do with recursive_directory_iterator, which iterates on directory_entry objects, not paths. Your confusion probably stems from the fact that directory entries are implicitly convertible to paths, so you can use them as such.
Your problem is really about path::string(), which, as the documentation states, uses the native format (i.e. with a platform dependent separator). You would get the same problem regardless of how you get your path.
If you want to get / as the directory separator, use path::generic_string() instead to get the path in generic format.
for(auto& dir_entry: fs::recursive_directory_iterator(dir_path))
{
if(dir_entry.is_directory()) continue;
string path_str = dir_entry.path().generic_string();
}
WindowsError: [Error 3] The system cannot find the path specified (when path too long?)
I'm making a script to find unique files between two directories. In order to do this, I use os.walk() to walk through the files, and if files of the same size exist, I hash them to ensure they are the same (opening the files in the process). The problem is that some files produce the above-mentioned error when opened. The most common reason people run into this problem is because the path is not correctly joined, thereby causing the script to try and open a file that doesn't exist. This isn't the case for me.
After trying different combinations of directories, I began to notice a pattern whereby files that produce the error seem to have a deep directory structure and a long filename. I can't think of any other reason for the issue - there are no character-encoding errors (I decode all my paths to UTF-8) and the paths do exist by virtue of os.walk().
My walk code:
for root, dirs, files in os.walk(directory):
for filename in files:
file_path = os.path.join(root, filename)
My hashing code:
def hash(file_path):
with open(dir_file, 'rb') as f:
hasher = hashlib.md5()
while True:
buf = f.read(byte_size)
if buf != '':
hasher.update(buf)
else:
break
result = hasher.hexdigest()
return result
Edit: The most recent path where the issue appeared was 5 directories deep (containing 142 characters, accounting for double backslashes), and the filename was an additional 122 characters long
That's due to Windows API file path size limitation as explained on MSDN:
In the Windows API (with some exceptions discussed in the following paragraphs), the maximum length for a path is MAX_PATH, which is defined as 260 characters. A local path is structured in the following order: drive letter, colon, backslash, name components separated by backslashes, and a terminating null character. For example, the maximum path on drive D is "D:\some 256-character path string" where "" represents the invisible terminating null character for the current system codepage. (The characters < > are used here for visual clarity and cannot be part of a valid path string.)
As also explained on that page, newer versions of Windows support extended file path prefix (\\?\) used for Unicode paths and such, but that's not a consistent or guaranteed behavior i.e. it doesn't mean it will work in all cases.
Either way, try prepending your path with the extended path prefix and see if it works for your case:
file_path = "\\\\?\\" + os.path.join(root, filename)
I am new to c++. The problem i am facing is with CreateDirectory method.
CreateDirectory("\\ServerName\foldername\",NULL) gives no error but it also doesn't create any directory. However if I write "D:\foldername" instead of "\\ServerName\foldername\" it works perfectly fine.
Any help would be highly appreciable.
"\ServerName\foldername\" is not a valid Windows path
"\\ServerName\foldername\" is valid, but this is the name of the "foldername" share on the "ServerName" network host. This is still not a valid directory you can create.
If ServerName is a valid host name, and if sharename, is a valid share on that host, on which you have write rights, then you could create "\\ServerName\sharename\foldername". But you can't create "\\ServerName\foldername\"
Thanks to Matteo for pointing out that in C strings, the \ must be escaped to \\
You must understand the difference between Fully Qualified vs Relative Paths
For Windows API functions that manipulate files, file names can often be relative to the current directory, while some APIs require a fully qualified path. A file name is relative to the current directory if it does not begin with one of the following:
A UNC name of any format, which always start with two backslash
characters ("\")
A disk designator with a backslash, for example "C:\" or "d:\".
A single backslash, for example, "\directory" or "\file.txt". This is
also referred to as an absolute path.
for more Information refer Naming Files, Paths, and Namespaces
I have xml profiles stored in a folder that are switched dynamically. But the behavior is absolute path and I need a relative path. The lua code is written to work with both windows paths (back slashes) and with mac paths (forward slashes).
On my mac the path might be /folder/folder/profile1.xml. In normal application the program will return a file/location of profile1.xml. And it will find the next profile in the same folder.
If I direct the application to a new folder using a relative link such as ../profile2.xml then the program will find the new profile and returns the file/location as ../profile2.xml. Then it will not find the next profile within the same folder... it's either looking for the next profile out a step (../) or within the original folder as set by the application. I want it to find the next requested profile within this new folder location.
The existing code that sets the current profile and profile path is this:
local loadedprofile = '' --set by application
local profilepath = '' --set by application and modified below
The relevant switching functions seem to be:
local function setDirectory(value)
profilepath = value
end
local function setFile(value)
if loadedprofile ~= value then
doprofilechange(value)
end
end
local function setFullPath(value)
local path, profile = value:match("(.-)([^\\/]-%.?([^%.\\/]*))$")
profilepath = path
if profile ~= loadedprofile then
doprofilechange(profile)
end
I'm thinking I might need to modify the match criteria of the third function to remove the ../. Maybe something like this removing the optional .'s
local function setFullPath(value)
local path, profile = value:match("(.-)([^\\/]-([^%.\\/]*))$")
profilepath = path
if profile ~= loadedprofile then
doprofilechange(profile)
end
I really have no clue as to how to write this code, I'm just trying to tweak this open source code (MIDI2LR) to suit my needs. In my rudimentary understanding of the code it seems the match criteria is overly convoluted. But I would like to know if I am reading it right. I interpret it as:
:match("(.-)([^\\/]-%.?([^%.\\/]*))$")
(.-) --minimal return
( )$ --from the end of profile path
[^\\/]- --starts with \ or \\ or /, 0 or more occurrences first result
%.? --through, with dots optional
[^%.\\/]* --starts with . or \ or \\ or /, 0 or more occurrences all results
If I am reading it right it would seem the first "starts with" is entirely redundant, or that the "from the end" should be associated with the second "starts with."
I have commented out the setFullPath function without the desired results which makes me think that a match requirement might be needed added to the setDirectory function.
Any help is greatly appreciated as I am in way over my head. Thanks!
Your reading of the match is incorrect, here is a more accurate version:
:match("(.-)([^\\/]-%.?([^%.\\/]*))$")
(.-) -- Match and grab everything up until the first non slash character
( )$ -- Grab everything up until the end
[^\\/]- -- Starts with any character OTHER THAN \ or /, 0 or more occurrences first result
%.? -- single dot optional in the middle of the name (for dot in something.ext)
[^%.\\/]* -- Any character OTHER THAN . or \ or /, 0 or more occurrences
A few notes - %. is a literal dot. [^xyz] is the inverse class, so every character other than x, y, or z. \\ is actually just one backslash, this is due to the escaping in a string.
This simpler version would break it in a similar way that is easier to work with: value:match("(.-)([^\\/]+)$")
You may want to provide more information on the profile loading behavior, its hard to tell what you need the code to do. What value would path and profile have in the example you give?
A user input string for a destination path can potentially contain spaces or other invalid characters.
Example: " C:\users\username\ \directoryname\ "
Note that this has whitespace on both sides of the path as well as an invalid folder name of just a space in the middle. Checking to see if it is an absolute path is insufficient because that only really handles the leading whitespace. Removing trailing whitespace is also insufficient because you're still left with the invalid space-for-folder-name in the middle.
How do i prove that the path is valid before I attempt to do anything with it?
The only way to "prove" the path is valid is to open it.
SHLWAPI provides a set of path functions which can be used to canonicalize the path or verify that a path seems to be valid. This can be useful to reject obviously bad paths but you still cannot trust that the path is valid without going through the file system.
With NTFS, I believe the path you give is actually valid (though Explorer may not allow you to create a directory with only a space.)
The Boost Filesystem library provides helpers to manipulate files, paths and so... Take a look at the simple ls example and the exists function.
I use GetFileAttributes for checking for existence. Works for both folders (look for the FILE_ATTRIBUTE_DIRECTORY flag in the returned value) and for files. I've done this for years, never had a problem.
If you don't want to open the file you can also use something like the access() function on POSIX-like platforms or _access() and friends on Windows. However, I like the Boost.Filesystem method Ricardo pointed out.