Skip to content

Commit 437aa0f

Browse files
clnowackijrobble
andauthored
Add utility to parse a list of strings from a single string (#128)
Co-authored-by: jrobble <jrobble@mitre.org>
1 parent d4fccdd commit 437aa0f

4 files changed

Lines changed: 170 additions & 3 deletions

File tree

detection/utils/include/Utils.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,10 @@ namespace MPF { namespace COMPONENT { namespace Utils {
5353
// not be used.
5454
std::string expandFileName(const std::string &filename, std::string &exp_filename);
5555

56+
// This function takes a string that contains a list of semi-colon delimited strings and
57+
// parses it into a vector of strings. The parsing checks for escaped delimiters.
58+
std::vector<std::string> ParseListFromString(const std::string &listToParse);
59+
5660
void trim(std::string &str);
5761

5862
cv::Mat ConvertToGray(const cv::Mat &image);

detection/utils/src/Utils.cpp

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -111,6 +111,48 @@ namespace MPF { namespace COMPONENT { namespace Utils {
111111
return "";
112112
}
113113

114+
vector<string> ParseListFromString(const string &listOfStrings) {
115+
if (listOfStrings.empty())
116+
return {};
117+
if ((listOfStrings.find(';') == string::npos) &&
118+
(listOfStrings.find('\\') == string::npos)) {
119+
// Nothing to parse; return the original string
120+
return {listOfStrings};
121+
}
122+
vector<string> values;
123+
string currentSegment;
124+
bool inEscapeSequence = false;
125+
for (int i = 0; i < listOfStrings.length(); ++i) {
126+
char ch = listOfStrings.at(i);
127+
if (inEscapeSequence) {
128+
inEscapeSequence = false;
129+
currentSegment += ch;
130+
continue;
131+
}
132+
switch (ch) {
133+
case '\\':
134+
inEscapeSequence = true;
135+
break;
136+
case ';':
137+
trim(currentSegment);
138+
if ( !currentSegment.empty() ) {
139+
values.push_back(currentSegment);
140+
}
141+
currentSegment.clear();
142+
break;
143+
default:
144+
currentSegment += ch;
145+
}
146+
}
147+
148+
if (!currentSegment.empty()) {
149+
trim(currentSegment);
150+
if (!currentSegment.empty()) {
151+
values.push_back(currentSegment);
152+
}
153+
}
154+
return values;
155+
}
114156

115157
void trim(std::string &str) {
116158
boost::trim(str);

detection/utils/test/CMakeLists.txt

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -33,8 +33,13 @@ set(CMAKE_CXX_STANDARD 17)
3333
find_package(GTest)
3434
if (${GTEST_FOUND})
3535
enable_testing()
36-
add_executable(DetectionComponentUtilsTest test_models_ini_parser.cpp)
37-
target_link_libraries(DetectionComponentUtilsTest GTest::GTest GTest::Main mpfComponentUtils mpfDetectionComponentApi)
36+
add_executable(ModelsIniParserTest test_models_ini_parser.cpp)
37+
target_link_libraries(ModelsIniParserTest GTest::GTest GTest::Main
38+
mpfComponentUtils mpfDetectionComponentApi)
39+
add_test(NAME ModelsIniParserTest COMMAND ModelsIniParserTest)
3840

39-
add_test(NAME DetectionComponentUtilsTest COMMAND DetectionComponentUtilsTest)
41+
add_executable(ParseListFromStringTest test_parse_list_from_string.cpp)
42+
target_link_libraries(ParseListFromStringTest GTest::GTest GTest::Main
43+
mpfComponentUtils mpfDetectionComponentApi)
44+
add_test(NAME ParseListFromStringTest COMMAND ParseListFromStringTest)
4045
endif()
Lines changed: 116 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,116 @@
1+
/******************************************************************************
2+
* NOTICE *
3+
* *
4+
* This software (or technical data) was produced for the U.S. Government *
5+
* under contract, and is subject to the Rights in Data-General Clause *
6+
* 52.227-14, Alt. IV (DEC 2007). *
7+
* *
8+
* Copyright 2024 The MITRE Corporation. All Rights Reserved. *
9+
******************************************************************************/
10+
11+
/******************************************************************************
12+
* Copyright 2024 The MITRE Corporation *
13+
* *
14+
* Licensed under the Apache License, Version 2.0 (the "License"); *
15+
* you may not use this file except in compliance with the License. *
16+
* You may obtain a copy of the License at *
17+
* *
18+
* http://www.apache.org/licenses/LICENSE-2.0 *
19+
* *
20+
* Unless required by applicable law or agreed to in writing, software *
21+
* distributed under the License is distributed on an "AS IS" BASIS, *
22+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. *
23+
* See the License for the specific language governing permissions and *
24+
* limitations under the License. *
25+
******************************************************************************/
26+
27+
#include <gtest/gtest.h>
28+
#include <log4cxx/basicconfigurator.h>
29+
30+
#include <Utils.h>
31+
32+
33+
using namespace std;
34+
using namespace MPF;
35+
using namespace COMPONENT;
36+
37+
bool init_logging() {
38+
log4cxx::BasicConfigurator::configure();
39+
return true;
40+
}
41+
bool logging_initialized = init_logging();
42+
43+
TEST(ParseListFromString, ParseEmptyList) {
44+
string test_string;
45+
vector<string> result = Utils::ParseListFromString(test_string);
46+
ASSERT_TRUE(result.empty());
47+
}
48+
49+
TEST(ParseListFromString, ParseListWithSingleString) {
50+
string test_string("Hello");
51+
vector<string> result = Utils::ParseListFromString(test_string);
52+
ASSERT_EQ(result.size(), 1);
53+
ASSERT_TRUE(result[0] == test_string);
54+
}
55+
56+
TEST(ParseListFromString, ParseDelimitedList) {
57+
string test_string("Hey;Hello;World");
58+
vector<string> result = Utils::ParseListFromString(test_string);
59+
ASSERT_EQ(result.size(), 3);
60+
ASSERT_TRUE(result[0] == "Hey");
61+
ASSERT_TRUE(result[1] == "Hello");
62+
ASSERT_TRUE(result[2] == "World");
63+
}
64+
65+
TEST(ParseListFromString, ParseListWithEscapedDelimiter) {
66+
string test_string("Hey;Hello\\;World");
67+
vector<string> result = Utils::ParseListFromString(test_string);
68+
ASSERT_EQ(result.size(), 2);
69+
ASSERT_TRUE(result[0] == "Hey");
70+
ASSERT_TRUE(result[1] == "Hello;World");
71+
}
72+
73+
TEST(ParseListFromString, ParseListWithUnnecessaryDoubleBackslash) {
74+
string test_string("Hey\\Hello;World");
75+
vector<string> result = Utils::ParseListFromString(test_string);
76+
ASSERT_EQ(result.size(), 2);
77+
ASSERT_TRUE(result[0] == "HeyHello");
78+
ASSERT_TRUE(result[1] == "World");
79+
}
80+
81+
TEST(ParseListFromString, ParseListWithQuadrupleBackslash) {
82+
string test_string("Hey\\\\Hello;World");
83+
vector<string> result = Utils::ParseListFromString(test_string);
84+
ASSERT_EQ(result.size(), 2);
85+
ASSERT_TRUE(result[0] == "Hey\\Hello");
86+
ASSERT_TRUE(result[1] == "World");
87+
}
88+
89+
TEST(ParseListFromString, ParseListWithNewlines) {
90+
string test_string("Hey\nHello;World\\\nFoo\\nBar");
91+
vector<string> result = Utils::ParseListFromString(test_string);
92+
ASSERT_EQ(result.size(), 2);
93+
ASSERT_TRUE(result[0] == "Hey\nHello");
94+
ASSERT_TRUE(result[1] == "World\nFoonBar");
95+
}
96+
97+
TEST(ParseListFromString, ParseListWith8BackslashesAndNewline) {
98+
string test_string("Hey\\\\\\\\\nHello;World");
99+
vector<string> result = Utils::ParseListFromString(test_string);
100+
ASSERT_EQ(result.size(), 2);
101+
ASSERT_TRUE(result[0] == "Hey\\\\\nHello");
102+
ASSERT_TRUE(result[1] == "World");
103+
}
104+
105+
TEST(ParseListFromString, ParseListWithExtraDelimiter) {
106+
string test_string("Hello;;World");
107+
vector<string> result = Utils::ParseListFromString(test_string);
108+
ASSERT_EQ(result.size(), 2);
109+
ASSERT_TRUE(result[0] == "Hello");
110+
ASSERT_TRUE(result[1] == "World");
111+
}
112+
113+
int main(int argc, char **argv) {
114+
testing::InitGoogleTest(&argc, argv);
115+
return RUN_ALL_TESTS();
116+
}

0 commit comments

Comments
 (0)