Namespaces
Variants
Views
Actions

Difference between revisions of "cpp/filesystem/path/lexically normal"

From cppreference.com
< cpp‎ | filesystem‎ | path
m (made the sample compile by adding a missing using statement)
m (fmt, {{c}}, {{range}}, headers sorted)
 
(13 intermediate revisions by 7 users not shown)
Line 2: Line 2:
 
{{cpp/filesystem/path/navbar}}
 
{{cpp/filesystem/path/navbar}}
 
{{dcl begin}}
 
{{dcl begin}}
{{dcl | since=c++17 | num=1 | 1=
+
{{dcl|since=c++17|num=1|1=
 
path lexically_normal() const;
 
path lexically_normal() const;
 
}}
 
}}
{{dcl | since=c++17 | num=2 | 1=
+
{{dcl|since=c++17|num=2|1=
path lexically_relative(const path& base) const;
+
path lexically_relative( const path& base ) const;
 
}}
 
}}
{{dcl | since=c++17 | num=3 | 1=
+
{{dcl|since=c++17|num=3|1=
path lexically_proximate(const path& base) const;
+
path lexically_proximate( const path& base ) const;
 
}}
 
}}
 
{{dcl end}}
 
{{dcl end}}
  
@1@ Returns {{tt|*this}} converted to [[cpp/filesystem/path|normal form]] in its generic format
+
@1@ Returns {{c|*this}} converted to [[cpp/filesystem/path|normal form]] in its generic format.
@2@ Returns {{tt|*this}} made relative to {{tt|base}}.  
+
@2@ Returns {{c|*this}} made relative to {{c|base}}.  
:* First, if {{c|root_name() !{{=}} base.root_name() {{!!}} is_absolute() !{{=}} base.is_absolute() {{!!}} (!has_root_directory() && base.has_root_directory())}}, returns a default-constructed path.
+
:* First, if {{c|1=root_name() != base.root_name()}} is {{c|true}} or {{c|1=is_absolute() != base.is_absolute()}} is {{c|true}} or {{c|(!has_root_directory() && base.has_root_directory())}} is {{c|true}} or any filename in {{c|relative_path()}} or {{c|base.relative_path()}} can be interpreted as a {{spar|root-name}}, returns a default-constructed path.
:* Otherwise, first determines the first mismatched element of {{tt|*this}} and {{tt|base}} as if by {{c|auto [a, b] {{=}} mismatch(begin(), end(), base.begin(), base.end())}}, then
+
:* Otherwise, first determines the first mismatched element of {{c|*this}} and {{c|base}} as if by {{c|auto [a, b] {{=}} mismatch(begin(), end(), base.begin(), base.end())}}, then
::* if {{c|a {{==}} end()}} and {{c|b {{==}} base.end()}}, returns {{c|path(".")}};
+
::* if {{c|a {{==}} end()}} and {{c|b {{==}} base.end()}}, returns {{c|path(".")}},
::* otherwise, if the number of {{spar|dot-dot}} filename elements in {{c|[b, base.end())}} is greater than the number of filename elements that are neither {{spar|dot}} nor {{spar|dot-dot}}, returns a default-constructed path.
+
::* otherwise, define {{math|N}} as the number of nonempty filename elements that are neither {{spar|dot}} nor {{spar|dot-dot}} in {{c|[b, base.end())}}, minus the number of {{spar|dot-dot}} filename elements, If {{math|N < 0}}, returns a default-constructed path,
 +
::* otherwise, if {{math|N {{=}} 0}} and {{c|a {{==}} end() {{!!}} a->empty()}}, returns {{c|path(".")}},
 
::* otherwise returns an object composed from
 
::* otherwise returns an object composed from
 
:::* a default-constructed {{c|path()}} followed by
 
:::* a default-constructed {{c|path()}} followed by
:::* as many applications of {{c|operator/{{=}}(path(".."))}} as there were filename elements in {{c|[b, base.end())}} that are neither {{spar|dot}} nor {{spar|dot-dot}} minus number of {{spar|dot-dot}} elements in that range, followed by
+
:::* {{math|N}} applications of {{c|operator/{{=}}(path(".."))}}, followed by
:::* one application of {{c|1=operator/=}} for each element in the half-open range {{tt|[a, end())}}
+
:::* one application of {{c|1=operator/=}} for each element in the half-open range {{range|a|end()}}.
@3@ If the value of {{c|lexically_relative(base)}} is not an empty path, return it. Otherwise return {{tt|*this}}.
+
@3@ If the value of {{c|lexically_relative(base)}} is not an empty path, return it. Otherwise return {{c|*this}}.
  
 
===Parameters===
 
===Parameters===
Line 29: Line 30:
  
 
===Return value===
 
===Return value===
@1@ The normal form of the path
+
@1@ The normal form of the path.
@2@ The relative form of the path
+
@2@ The relative form of the path.
@3@ The proximate form of the path
+
@3@ The proximate form of the path.
  
===Exceptions===
+
{{cpp/impldef exception}}
(none)
+
  
 
===Notes===
 
===Notes===
 
These conversions are purely lexical. They do not check that the paths exist, do not follow symlinks, and do not access the filesystem at all. For symlink-following counterparts of {{tt|lexically_relative}} and {{tt|lexically_proximate}}, see {{ltt|cpp/filesystem/relative|relative}} and {{ltt|cpp/filesystem/relative|proximate}}.
 
These conversions are purely lexical. They do not check that the paths exist, do not follow symlinks, and do not access the filesystem at all. For symlink-following counterparts of {{tt|lexically_relative}} and {{tt|lexically_proximate}}, see {{ltt|cpp/filesystem/relative|relative}} and {{ltt|cpp/filesystem/relative|proximate}}.
  
On Windows, the returned path has backslashes (the preferred separators),  
+
On Windows, the returned {{tt|path}} has backslashes (the preferred separators).
 +
 
 +
On POSIX, no filename in a relative path is acceptable as a {{spar|root-name}}.
  
 
===Example===
 
===Example===
{{example|code=
+
{{example
#include <iostream>
+
|code=
#include <filesystem>
+
 
#include <cassert>
 
#include <cassert>
 +
#include <filesystem>
 +
#include <iostream>
 
namespace fs = std::filesystem;
 
namespace fs = std::filesystem;
using fs::path;
 
  
 
int main()
 
int main()
 
{
 
{
     assert(path("foo/./bar/..").lexically_normal() == "foo/");
+
     assert(fs::path("a/./b/..").lexically_normal() == "a/");
     assert(path("foo/.///bar/../").lexically_normal() == "foo/");
+
     assert(fs::path("a/.///b/../").lexically_normal() == "a/");
 
+
     assert(fs::path("/a/d").lexically_relative("/a/b/c") == "../../d");
     assert(path("/a/d").lexically_relative("/a/b/c") == "../../d");
+
     assert(fs::path("/a/b/c").lexically_relative("/a/d") == "../b/c");
     assert(path("/a/b/c").lexically_relative("/a/d") == "../b/c");
+
     assert(fs::path("a/b/c").lexically_relative("a") == "b/c");
     assert(path("a/b/c").lexically_relative("a") == "b/c");
+
     assert(fs::path("a/b/c").lexically_relative("a/b/c/x/y") == "../..");
     assert(path("a/b/c").lexically_relative("a/b/c/x/y") == "../..");
+
     assert(fs::path("a/b/c").lexically_relative("a/b/c") == ".");
     assert(path("a/b/c").lexically_relative("a/b/c") == ".");
+
     assert(fs::path("a/b").lexically_relative("c/d") == "../../a/b");
     assert(path("a/b").lexically_relative("c/d") == "../../a/b");
+
    assert(fs::path("a/b").lexically_relative("/a/b") == "");
 +
    assert(fs::path("a/b").lexically_proximate("/a/b") == "a/b");
 
}
 
}
 
}}
 
}}
 +
 +
===Defect reports===
 +
{{dr list begin}}
 +
{{dr list item|wg=lwg|dr=3070|std=c++17|before=a filename that can also be a root-name may cause surprising result|after=treated as error case}}
 +
{{dr list item|wg=lwg|dr=3096|std=c++17|before=trailing "/" and "/." are handled incorrectly|after=corrected}}
 +
{{dr list end}}
  
 
===See also===
 
===See also===
 
{{dsc begin}}
 
{{dsc begin}}
{{dsc inc | cpp/filesystem/dsc relative}}
+
{{dsc inc|cpp/filesystem/dsc relative}}
 
{{dsc end}}
 
{{dsc end}}
  
{{langlinks|zh}}
+
{{langlinks|de|es|ja|ru|zh}}

Latest revision as of 22:24, 5 September 2023

 
 
 
 
path lexically_normal() const;
(1) (since C++17)
path lexically_relative( const path& base ) const;
(2) (since C++17)
path lexically_proximate( const path& base ) const;
(3) (since C++17)
1) Returns *this converted to normal form in its generic format.
2) Returns *this made relative to base.
  • First, if root_name() != base.root_name() is true or is_absolute() != base.is_absolute() is true or (!has_root_directory() && base.has_root_directory()) is true or any filename in relative_path() or base.relative_path() can be interpreted as a root-name, returns a default-constructed path.
  • Otherwise, first determines the first mismatched element of *this and base as if by auto [a, b] = mismatch(begin(), end(), base.begin(), base.end()), then
  • if a == end() and b == base.end(), returns path("."),
  • otherwise, define N as the number of nonempty filename elements that are neither dot nor dot-dot in [b, base.end()), minus the number of dot-dot filename elements, If N < 0, returns a default-constructed path,
  • otherwise, if N = 0 and a == end() || a->empty(), returns path("."),
  • otherwise returns an object composed from
  • a default-constructed path() followed by
  • N applications of operator/=(path("..")), followed by
  • one application of operator/= for each element in the half-open range [aend()).
3) If the value of lexically_relative(base) is not an empty path, return it. Otherwise return *this.

Contents

[edit] Parameters

(none)

[edit] Return value

1) The normal form of the path.
2) The relative form of the path.
3) The proximate form of the path.

[edit] Exceptions

May throw implementation-defined exceptions.

[edit] Notes

These conversions are purely lexical. They do not check that the paths exist, do not follow symlinks, and do not access the filesystem at all. For symlink-following counterparts of lexically_relative and lexically_proximate, see relative and proximate.

On Windows, the returned path has backslashes (the preferred separators).

On POSIX, no filename in a relative path is acceptable as a root-name.

[edit] Example

#include <cassert>
#include <filesystem>
#include <iostream>
namespace fs = std::filesystem;
 
int main()
{
    assert(fs::path("a/./b/..").lexically_normal() == "a/");
    assert(fs::path("a/.///b/../").lexically_normal() == "a/");
    assert(fs::path("/a/d").lexically_relative("/a/b/c") == "../../d");
    assert(fs::path("/a/b/c").lexically_relative("/a/d") == "../b/c");
    assert(fs::path("a/b/c").lexically_relative("a") == "b/c");
    assert(fs::path("a/b/c").lexically_relative("a/b/c/x/y") == "../..");
    assert(fs::path("a/b/c").lexically_relative("a/b/c") == ".");
    assert(fs::path("a/b").lexically_relative("c/d") == "../../a/b");
    assert(fs::path("a/b").lexically_relative("/a/b") == "");
    assert(fs::path("a/b").lexically_proximate("/a/b") == "a/b");
}

[edit] Defect reports

The following behavior-changing defect reports were applied retroactively to previously published C++ standards.

DR Applied to Behavior as published Correct behavior
LWG 3070 C++17 a filename that can also be a root-name may cause surprising result treated as error case
LWG 3096 C++17 trailing "/" and "/." are handled incorrectly corrected

[edit] See also

composes a relative path
(function) [edit]