diff --git a/src/App/ElementNamingUtils.cpp b/src/App/ElementNamingUtils.cpp index f43ea20a7e..836bf6b238 100644 --- a/src/App/ElementNamingUtils.cpp +++ b/src/App/ElementNamingUtils.cpp @@ -1,6 +1,7 @@ // SPDX-License-Identifier: LGPL-2.1-or-later #include "ElementNamingUtils.h" +#include #include @@ -123,3 +124,20 @@ const std::string Data::indexSuffix(int index, const char* label) name += std::to_string(index); return name; } + +int Data::indexOfElement(std::string_view s, std::string_view prefix) +{ + int idx = 0; + if (prefix.empty()) { + return idx; + } + if (s.starts_with(prefix)) { + auto num = s.substr(prefix.size()); + auto [ptr, ec] = std::from_chars(num.data(), num.data() + num.size(), idx); + + if (ec != std::errc{} || ptr != num.data() + num.size()) { + return 0; + } + } + return idx; +} diff --git a/src/App/ElementNamingUtils.h b/src/App/ElementNamingUtils.h index 9728aab195..4b284c65ef 100644 --- a/src/App/ElementNamingUtils.h +++ b/src/App/ElementNamingUtils.h @@ -3,6 +3,7 @@ #pragma once #include +#include #include #include "FCGlobal.h" @@ -119,5 +120,8 @@ AppExport const char *hasMappedElementName(const char *subname); AppExport const std::string indexSuffix(int index, const char *label=ELEMENT_MAP_INDEX); +/// Given a string and a prefix, return the integer index that follows that prefix, or 0 if not found +AppExport int indexOfElement(std::string_view s, std::string_view prefix); + } // namespace Data // clang-format on diff --git a/src/Mod/Part/App/PartFeature.cpp b/src/Mod/Part/App/PartFeature.cpp index dd1d3ba3b7..1047e5fc8a 100644 --- a/src/Mod/Part/App/PartFeature.cpp +++ b/src/Mod/Part/App/PartFeature.cpp @@ -1987,9 +1987,8 @@ void FilletBase::onUpdateElementReference(const App::Property* prop) FC_WARN("fillet edge count mismatch in object " << getFullName()); break; } - int idx = 0; - sscanf(subs[i].c_str(), "Edge%d", &idx); - if (idx) { + int idx = Data::indexOfElement(subs[i], "Edge"); + if (idx != 0) { values[i].edgeid = idx; } else { diff --git a/src/Mod/Part/Gui/DlgFilletEdges.cpp b/src/Mod/Part/Gui/DlgFilletEdges.cpp index 2f5016a04e..c8a4c4f375 100644 --- a/src/Mod/Part/Gui/DlgFilletEdges.cpp +++ b/src/Mod/Part/Gui/DlgFilletEdges.cpp @@ -48,6 +48,7 @@ #include #include #include +#include #include #include #include @@ -674,8 +675,7 @@ void DlgFilletEdges::setupFillet(const std::vector& objs) for (size_t i = 0; i < e.size(); ++i) { auto& sub = subs[i]; if (sub.newName.empty()) { - int idx = 0; - sscanf(sub.oldName.c_str(), "Edge%d", &idx); + int idx = Data::indexOfElement(sub.oldName, "Edge"); if (idx == 0) { FC_WARN("missing element reference: " << sub.oldName); } diff --git a/tests/src/App/ElementNamingUtils.cpp b/tests/src/App/ElementNamingUtils.cpp index 5da0d3e0e0..62b2d43472 100644 --- a/tests/src/App/ElementNamingUtils.cpp +++ b/tests/src/App/ElementNamingUtils.cpp @@ -59,4 +59,29 @@ TEST_F(ElementNamingUtilsTest, findElementName) EXPECT_STREQ(name3, "Edge3"); EXPECT_STREQ(name4, "Edge4"); } + +TEST_F(ElementNamingUtilsTest, indexOfElement) +{ + // Valid cases + EXPECT_EQ(Data::indexOfElement("Edge1", "Edge"), 1); + EXPECT_EQ(Data::indexOfElement("Edge12", "Edge"), 12); + EXPECT_EQ(Data::indexOfElement("Edge999", "Edge"), 999); + + // Prefix mismatch + EXPECT_EQ(Data::indexOfElement("Node5", "Edge"), 0); + + // Missing numeric suffix + EXPECT_EQ(Data::indexOfElement("Edge", "Edge"), 0); + + // Non-numeric suffix + EXPECT_EQ(Data::indexOfElement("EdgeX", "Edge"), 0); + EXPECT_EQ(Data::indexOfElement("Edge12X", "Edge"), 0); + + // Zero or invalid index + EXPECT_EQ(Data::indexOfElement("Edge0", "Edge"), 0); + + // Degenerate inputs + EXPECT_EQ(Data::indexOfElement("", "Edge"), 0); + EXPECT_EQ(Data::indexOfElement("123", ""), 0); +} // NOLINTEND(readability-magic-numbers)