From 14b0bc7c09469ecb1348a1e3a3bf45a9b0ec2ea4 Mon Sep 17 00:00:00 2001 From: Alexander Gutkin Date: Tue, 26 May 2026 12:25:11 -0700 Subject: [PATCH] Add `Dirname` and `Extension` API to path manipulation library. PiperOrigin-RevId: 921615561 --- openfst/compat/file_path.cc | 22 ++++++++++++++++++++++ openfst/compat/file_path.h | 11 +++++++++++ openfst/compat/file_path_test.cc | 24 ++++++++++++++++++++++++ 3 files changed, 57 insertions(+) diff --git a/openfst/compat/file_path.cc b/openfst/compat/file_path.cc index 9b60e107..a56bb514 100644 --- a/openfst/compat/file_path.cc +++ b/openfst/compat/file_path.cc @@ -46,6 +46,20 @@ std::pair SplitPath(std::string_view path) { absl::ClippedSubstr(path, pos + 1)); } +// Return the parts of the basename of path, split on the final ".". +// If there is no "." in the basename or "." is the final character in the +// basename, the second value will be empty. +std::pair SplitBasename( + std::string_view path) { + path = Basename(path); + + const size_t pos = path.find_last_of('.'); + if (pos == std::string_view::npos) + return std::make_pair(path, absl::ClippedSubstr(path, path.size(), 0)); + return std::make_pair(path.substr(0, pos), + absl::ClippedSubstr(path, pos + 1)); +} + } // namespace std::string JoinPath(std::string_view path1, std::string_view path2) { @@ -81,4 +95,12 @@ std::string_view Basename(std::string_view path) { return SplitPath(path).second; } +std::string_view Dirname(absl::string_view path) { + return SplitPath(path).first; +} + +std::string_view Extension(std::string_view path) { + return SplitBasename(path).second; +} + } // namespace fst diff --git a/openfst/compat/file_path.h b/openfst/compat/file_path.h index e4ae40a0..e9f7b6ce 100644 --- a/openfst/compat/file_path.h +++ b/openfst/compat/file_path.h @@ -36,6 +36,17 @@ std::string JoinPathRespectAbsolute(std::string_view path1, // "/" in the path, the result is the same as the input. std::string_view Basename(std::string_view path); +// Returns the part of the path before the final "/", EXCEPT: +// * If there is a single leading "/" in the path, the result will be the +// leading "/". +// * If there is no "/" in the path, the result is the empty prefix of the +// input string. +std::string_view Dirname(std::string_view path); + +// Returns the part of the basename of path after the final ".". If +// there is no "." in the basename, the result is empty. +std::string_view Extension(std::string_view path); + } // namespace fst #endif // OPENFST_COMPAT_FILE_PATH_H_ diff --git a/openfst/compat/file_path_test.cc b/openfst/compat/file_path_test.cc index 2859afcd..afe1d11b 100644 --- a/openfst/compat/file_path_test.cc +++ b/openfst/compat/file_path_test.cc @@ -57,5 +57,29 @@ TEST(FilePathTest, CheckBasename) { EXPECT_EQ("hello", Basename("/a/b/c/hello")); } +TEST(FilePathTest, CheckDirname) { + EXPECT_EQ("/hello", Dirname("/hello/")); + EXPECT_EQ("/", Dirname("/hello")); + EXPECT_EQ("/hello", Dirname("/hello/world")); + EXPECT_EQ("hello", Dirname("hello/world")); + EXPECT_EQ("hello", Dirname("hello/")); + EXPECT_EQ("", Dirname("world")); + EXPECT_EQ("/", Dirname("/")); + EXPECT_EQ("", Dirname("")); +} + +TEST(FilePathTest, CheckExtension) { + EXPECT_EQ("gif", Extension("foo.gif")); + EXPECT_EQ("", Extension("foo.")); + EXPECT_EQ("", Extension("")); + EXPECT_EQ("", Extension("/")); + EXPECT_EQ("", Extension("foo")); + EXPECT_EQ("", Extension("foo/")); + EXPECT_EQ("gif", Extension("/a/path/to/foo.gif")); + EXPECT_EQ("html", Extension("/a/path.bar/to/foo.html")); + EXPECT_EQ("", Extension("/a/path.bar/to/foo")); + EXPECT_EQ("baz", Extension("/a/path.bar/to/foo.bar.baz")); +} + } // namespace } // namespace fst