std::unique_ptr<IOBuf> buffer_;
};
+inline auto byLineImpl(File file, char delim, bool keepDelimiter)
+ -> decltype(fromFile(std::move(file))
+ | eachAs<StringPiece>()
+ | resplit(delim, keepDelimiter)) {
+ return fromFile(std::move(file))
+ | eachAs<StringPiece>()
+ | resplit(delim, keepDelimiter);
+}
+
} // !detail
/**
* Note: This produces StringPieces which reference temporary strings which are
* only valid during iteration.
*/
+inline auto byLineFull(File file, char delim = '\n')
+ -> decltype(detail::byLineImpl(std::move(file), delim, true)) {
+ return detail::byLineImpl(std::move(file), delim, true);
+}
+
+inline auto byLineFull(int fd, char delim = '\n')
+ -> decltype(byLineFull(File(fd), delim)) {
+ return byLineFull(File(fd), delim);
+}
+
+inline auto byLineFull(const char* f, char delim = '\n')
+ -> decltype(byLineFull(File(f), delim)) {
+ return byLineFull(File(f), delim);
+}
+
inline auto byLine(File file, char delim = '\n')
- -> decltype(fromFile(std::move(file))
- | eachAs<StringPiece>()
- | resplit(delim)) {
- return fromFile(std::move(file))
- | eachAs<StringPiece>()
- | resplit(delim);
+ -> decltype(detail::byLineImpl(std::move(file), delim, false)) {
+ return detail::byLineImpl(std::move(file), delim, false);
}
inline auto byLine(int fd, char delim = '\n')
inline auto byLine(const char* f, char delim = '\n')
-> decltype(byLine(File(f), delim)) { return byLine(File(f), delim); }
-
}} // !folly::gen
class StringResplitter : public Operator<StringResplitter> {
char delimiter_;
+ bool keepDelimiter_;
+
public:
- explicit StringResplitter(char delimiter) : delimiter_(delimiter) { }
+ explicit StringResplitter(char delimiter, bool keepDelimiter = false)
+ : delimiter_(delimiter), keepDelimiter_(keepDelimiter) {}
template <class Source>
class Generator : public GenImpl<StringPiece, Generator<Source>> {
Source source_;
char delimiter_;
+ bool keepDelimiter_;
+
public:
- Generator(Source source, char delimiter)
- : source_(std::move(source)), delimiter_(delimiter) { }
+ Generator(Source source, char delimiter, bool keepDelimiter)
+ : source_(std::move(source)),
+ delimiter_(delimiter),
+ keepDelimiter_(keepDelimiter) {}
template <class Body>
bool apply(Body&& body) const {
if (s.back() != this->delimiter_) {
return body(s);
}
- s.pop_back(); // Remove the 1-character delimiter
+ if (!keepDelimiter_) {
+ s.pop_back(); // Remove the 1-character delimiter
+ }
return body(s);
});
if (!source_.apply(splitter)) {
class Value,
class Gen = Generator<Source>>
Gen compose(GenImpl<Value, Source>&& source) const {
- return Gen(std::move(source.self()), delimiter_);
+ return Gen(std::move(source.self()), delimiter_, keepDelimiter_);
}
template<class Source,
class Value,
class Gen = Generator<Source>>
Gen compose(const GenImpl<Value, Source>& source) const {
- return Gen(source.self(), delimiter_);
+ return Gen(source.self(), delimiter_, keepDelimiter_);
}
};
*/
// make this a template so we don't require StringResplitter to be complete
// until use
-template <class S=detail::StringResplitter>
-S resplit(char delimiter) {
- return S(delimiter);
+template <class S = detail::StringResplitter>
+S resplit(char delimiter, bool keepDelimiter = false) {
+ return S(delimiter, keepDelimiter);
}
template <class S = detail::SplitStringSource<char>>
#include <string>
#include <vector>
+#include <folly/Array.h>
#include <folly/File.h>
#include <folly/Range.h>
#include <folly/experimental/TestUtil.h>
}
}
-class FileGenBufferedTest : public ::testing::TestWithParam<int> { };
+TEST(FileGen, ByLineFull) {
+ auto cases = std::vector<std::string> {
+ stripLeftMargin(R"(
+ Hello world
+ This is the second line
+
+
+ a few empty lines above
+ incomplete last line)"),
+
+ "complete last line\n",
+
+ "\n",
+
+ ""};
+
+ for (auto& lines : cases) {
+ test::TemporaryFile file("ByLineFull");
+ EXPECT_EQ(lines.size(), write(file.fd(), lines.data(), lines.size()));
+
+ auto found =
+ byLineFull(file.path().string().c_str()) | unsplit<std::string>("");
+
+ EXPECT_EQ(lines, found);
+ }
+}
+
+class FileGenBufferedTest : public ::testing::TestWithParam<int> {};
TEST_P(FileGenBufferedTest, FileWriter) {
size_t bufferSize = GetParam();
}
}
+TEST(StringGen, ResplitKeepDelimiter) {
+ auto collect = eachTo<std::string>() | as<vector>();
+ {
+ auto pieces =
+ from({"hello,, world, goodbye, meow"}) | resplit(',', true) | collect;
+ ASSERT_EQ(5, pieces.size());
+ EXPECT_EQ("hello,", pieces[0]);
+ EXPECT_EQ(",", pieces[1]);
+ EXPECT_EQ(" world,", pieces[2]);
+ EXPECT_EQ(" goodbye,", pieces[3]);
+ EXPECT_EQ(" meow", pieces[4]);
+ }
+ {
+ auto pieces = from({"hel", "lo,", ", world", ", goodbye, m", "eow"}) |
+ resplit(',', true) | collect;
+ ASSERT_EQ(5, pieces.size());
+ EXPECT_EQ("hello,", pieces[0]);
+ EXPECT_EQ(",", pieces[1]);
+ EXPECT_EQ(" world,", pieces[2]);
+ EXPECT_EQ(" goodbye,", pieces[3]);
+ EXPECT_EQ(" meow", pieces[4]);
+ }
+}
+
void checkResplitMaxLength(vector<string> ins,
char delim,
uint64_t maxLength,