C++ 用C+读取JSON文件+;促进
HTTP服务器向我发送JSON响应(字符串),如下所示:C++ 用C+读取JSON文件+;促进,c++,json,boost,tree,directory,C++,Json,Boost,Tree,Directory,HTTP服务器向我发送JSON响应(字符串),如下所示: { "folders" : [{ "id" : 109, "parent_id" : 110, "path" : "\/1\/105\/110\/" }, { "id" : 110, "parent_id" : 105, "path" : "\/1\
{
"folders" :
[{
"id" : 109,
"parent_id" : 110,
"path" : "\/1\/105\/110\/"
},
{
"id" : 110,
"parent_id" : 105,
"path" : "\/1\/105\/"
}
],
"files" :
[{
"id" : 26,
"parent_id" : 105,
"name" : "picture.png",
"hash" : "md5_hash",
"path" : "\/1\/105\/"
},
{
"id" : 25,
"parent_id" : 110,
"name" : "another_picture.jpg",
"hash" : "md5_hash",
"path" : "\/1\/105\/110\/"
}
]
}
folders:
:
id: 109
parent_id: 110
name: 2011_pictures
:
id: 110
parent_id: 105
name: Aminos
files:
id: 26
parent_id: 105
name: logo.png
:
id: 5
parent_id: 109
name: me.jpg
我想将这个“远程文件夹树”与本地文件夹树(例如,包含本地文件位置的字符串向量)进行比较,所以我想在(字符串,向量(映射(字符串,字符串)))的映射上转换这个JSON(我不知道这是否可行)
我正在开发一个工具来同步本地文件夹和远程文件夹之间的文件,因此我使用boost来列出本地文件夹,我想比较本地列表和远程列表(JSON响应),以生成操作(下载本地文件夹中不存在的缺失文件,上载远程文件夹中不存在的文件)
通过此功能,我发现了另一个问题:
void print(boost::property_tree::ptree const& pt)
{
using boost::property_tree::ptree;
ptree::const_iterator end = pt.end();
for (ptree::const_iterator it = pt.begin(); it != end; ++it)
{
std::cout << it->first << ": " << it->second.get_value<std::string>() << std::endl;
print(it->second);
}
}
我想知道是否有可能用这个结果生成一个映射
,它将有两个键:“文件夹”和“文件”,通过这两个键,我们可以访问包含每个对象(文件或文件夹)信息的类型映射向量。如果这是可行的,它将降低任务的复杂性(比较两个文件夹列表)
示例:T[“folder”][0][“id”]将返回“109”;T[“文件”][0][“名称”]将返回“logo.png”
<强>更新:这个问题已经过时了,但是我想给出一个建议:在你想在C++下处理JSON时,使用RAPIDJSON。<强> < /P> 免责声明:下面的示例不是完整的JSON解析器。考虑使用支持你需求的库。您可以在这里看到更先进的JSON解析器
快速而肮脏的精神语法(假设你不需要太多的一致性)是: text_ = '"' >> raw [*('\\' >> char_ | ~char_('"'))] >> '"'; // ¹
value_ = null | bool | text_ | double_ | object_ | array_; // ²
member_ = text_ >> ':' >> value_;
object_ = '{' >> -(member_ % ',') >> '}';
array_ = '[' >> -(value_ % ',') >> ']';
// ¹ as a bonus I added utf8 escape decoding in the full sample
// ² as another bonus I threw in the missing `null` and `bool` types
使用AST如:
,它不再转化为C++类型。using text = std::string;
using value = boost::make_recursive_variant<
null,
bool,
text, // "string" (roughly!)
double, // number
std::map<text, boost::recursive_variant_>, // object
std::vector<boost::recursive_variant_> // array
>::type;
using member = std::pair<text, value>;
using object = std::map<text, value>;
using array = std::vector<value>;
下面是一个演示程序:
更新的演示
演示已更新,以向您展示如何获得您在问题编辑中建议的“用户友好”数据结构:
int main() {
auto json = qd_json::parse(sample);
// extract into user friendly datastructure from the question:
auto extracted = Data::extract_from(json);
for (auto& e : extracted.folders) std::cout << "folder:\t" << e.id << "\t" << e.path << "\n";
for (auto& e : extracted.files) std::cout << "file:\t" << e.id << "\t" << e.path << "\t" << e.name << "\n";
}
使用Boost内置的json解析器来解析属性树:
因为另一个答案中的数据结构和目标数据结构是: 这种语法允许
- 不重要的空白
- 对象内属性的重新排序
- 和省略的对象属性
- 及早检查属性值类型
- 更低的编译时间
- 更少的代码:37更少的LoC(不包括~22%的样本JSON行)
#include <boost/fusion/adapted/std_pair.hpp>
#include <boost/spirit/include/qi.hpp>
#include <boost/spirit/include/phoenix.hpp>
#include <map>
namespace qi = boost::spirit::qi;
static std::string const sample = R"(
{
"folders" :
[{
"id" : 109,
"parent_id" : 110,
"path" : "\/1\/105\/110\/"
},
{
"id" : 110,
"parent_id" : 105,
"path" : "\/1\/105\/"
}
],
"files" :
[{
"id" : 26,
"parent_id" : 105,
"name" : "picture.png",
"hash" : "md5_hash",
"path" : "\/1\/105\/"
},
{
"id" : 25,
"parent_id" : 110,
"name" : "another_picture.jpg",
"hash" : "md5_hash",
"path" : "\/1\/105\/110\/"
}
]
})";
namespace qd_json { // quick and dirty JSON handling
struct null {
bool operator==(null) const { return true; }
};
inline static std::ostream& operator<<(std::ostream& os, null) { return os << "null"; }
using text = std::string;
using value = boost::make_recursive_variant<
null,
text, // "string" (roughly!)
double, // number
std::map<text, boost::recursive_variant_>, // object
std::vector<boost::recursive_variant_>, // array
bool
>::type;
using member = std::pair<text, value>;
using object = std::map<text, value>;
using array = std::vector<value>;
template <typename It, typename Skipper = qi::space_type>
struct grammar : qi::grammar<It, value(), Skipper>
{
grammar() : grammar::base_type(value_) {
using namespace qi;
text_ = '"' >> raw [*('\\' >> char_ | ~char_('"'))] >> '"';
null_ = "null" >> attr(null{});
bool_ = "true" >> attr(true) | "false" >> attr(false);
value_ = null_ | bool_ | text_ | double_ | object_ | array_;
member_ = text_ >> ':' >> value_;
object_ = '{' >> -(member_ % ',') >> '}';
array_ = '[' >> -(value_ % ',') >> ']';
////////////////////////////////////////
// Bonus: properly decoding the string:
text_ = lexeme [ '"' >> *ch_ >> '"' ];
ch_ = +(
~char_("\"\\")) [ _val += _1 ] |
qi::lit("\x5C") >> ( // \ (reverse solidus)
qi::lit("\x22") [ _val += '"' ] | // " quotation mark U+0022
qi::lit("\x5C") [ _val += '\\' ] | // \ reverse solidus U+005C
qi::lit("\x2F") [ _val += '/' ] | // / solidus U+002F
qi::lit("\x62") [ _val += '\b' ] | // b backspace U+0008
qi::lit("\x66") [ _val += '\f' ] | // f form feed U+000C
qi::lit("\x6E") [ _val += '\n' ] | // n line feed U+000A
qi::lit("\x72") [ _val += '\r' ] | // r carriage return U+000D
qi::lit("\x74") [ _val += '\t' ] | // t tab U+0009
qi::lit("\x75") // uXXXX U+XXXX
>> _4HEXDIG [ append_utf8(qi::_val, qi::_1) ]
);
BOOST_SPIRIT_DEBUG_NODES((text_)(value_)(member_)(object_)(array_)(null_)(bool_))
}
private:
qi::rule<It, text()> text_, ch_;
qi::rule<It, null()> null_;
qi::rule<It, bool()> bool_;
qi::rule<It, value(), Skipper> value_;
qi::rule<It, member(), Skipper> member_;
qi::rule<It, object(), Skipper> object_;
qi::rule<It, array(), Skipper> array_;
struct append_utf8_f {
template <typename...> struct result { typedef void type; };
template <typename String, typename Codepoint>
void operator()(String& to, Codepoint codepoint) const {
auto out = std::back_inserter(to);
boost::utf8_output_iterator<decltype(out)> convert(out);
*convert++ = codepoint;
}
};
boost::phoenix::function<append_utf8_f> append_utf8;
qi::uint_parser<uint32_t, 16, 4, 4> _4HEXDIG;
};
template <typename Range, typename It = typename boost::range_iterator<Range const>::type>
value parse(Range const& input) {
grammar<It> g;
It first(boost::begin(input)), last(boost::end(input));
value parsed;
bool ok = qi::phrase_parse(first, last, g, qi::space, parsed);
if (ok && (first == last))
return parsed;
throw std::runtime_error("Remaining unparsed: '" + std::string(first, last) + "'");
}
}
#include <boost/range/algorithm.hpp>
#include <boost/range/adaptors.hpp>
#include <boost/range/algorithm_ext/push_back.hpp>
struct Data {
struct Folder { int id, parent_id; std::string path; };
struct File { int id, parent_id; std::string path, name, md5_hash; };
using Folders = std::vector<Folder>;
using Files = std::vector<File>;
Folders folders;
Files files;
static Data extract_from(qd_json::value const& json) {
using namespace boost::adaptors;
return {
boost::copy_range<Folders>(arr(obj(json).at("folders")) | transformed(obj) | transformed(&Data::extract_folder)),
boost::copy_range<Files> (arr(obj(json).at("files")) | transformed(obj) | transformed(&Data::extract_file)),
};
}
private:
static Folder extract_folder(qd_json::object const& obj) {
return {
id (obj.at("id")),
id (obj.at("parent_id")),
text (obj.at("path"))
};
}
static File extract_file(qd_json::object const& obj) {
return {
id (obj.at("id")),
id (obj.at("parent_id")),
text (obj.at("path")),
text (obj.at("name")),
text (obj.at("hash")),
};
}
static int id (qd_json::value const&v) { return boost::get<double>(v); };
static std::string text(qd_json::value const&v) { return boost::get<qd_json::text>(v); };
static qd_json::array arr (qd_json::value const&v) { return boost::get<qd_json::array>(v); };
static qd_json::object obj (qd_json::value const&v) { return boost::get<qd_json::object>(v); };
};
int main()
{
auto json = qd_json::parse(sample);
// compare json documents
qd_json::value clone = json;
assert(json == clone);
// extract into user friendly datastructure from the question:
auto extracted = Data::extract_from(json);
for (auto& e : extracted.folders) std::cout << "folder:\t" << e.id << "\t" << e.path << "\n";
for (auto& e : extracted.files) std::cout << "file:\t" << e.id << "\t" << e.path << "\t" << e.name << "\n";
}
//#define BOOST_SPIRIT_DEBUG
#include <boost/fusion/adapted.hpp>
#include <boost/spirit/include/qi.hpp>
#include <boost/spirit/include/phoenix.hpp>
namespace qi = boost::spirit::qi;
static std::string const sample = R"(
{
"folders" :
[{
"id" : 109,
"parent_id" : 110,
"path" : "\/1\/105\/110\/"
},
{
"id" : 110,
"parent_id" : 105,
"path" : "\/1\/105\/"
}
],
"files" :
[{
"id" : 26,
"parent_id" : 105,
"name" : "picture.png",
"hash" : "md5_hash",
"path" : "\/1\/105\/"
},
{
"id" : 25,
"parent_id" : 110,
"name" : "another_picture.jpg",
"hash" : "md5_hash",
"path" : "\/1\/105\/110\/"
}
]
})";
struct Data {
struct Folder { int id, parent_id; std::string path; };
struct File { int id, parent_id; std::string path, name, md5_hash; };
using Folders = std::vector<Folder>;
using Files = std::vector<File>;
Folders folders;
Files files;
};
BOOST_FUSION_ADAPT_STRUCT(Data::Folder, (int,id)(int,parent_id)(std::string,path))
BOOST_FUSION_ADAPT_STRUCT(Data::File, (int,id)(int,parent_id)(std::string,path)(std::string,name)(std::string,md5_hash))
BOOST_FUSION_ADAPT_STRUCT(Data, (Data::Folders,folders)(Data::Files,files))
namespace folder_info { // adhoc JSON parser
template <typename It, typename Skipper = qi::space_type>
struct grammar : qi::grammar<It, Data(), Skipper>
{
grammar() : grammar::base_type(start) {
using namespace qi;
start = '{' >>
(folders_ >> commasep) ^
(files_ >> commasep)
>> '}';
folders_ = prop_key(+"folders") >> '[' >> -(folder_ % ',') >> ']';
files_ = prop_key(+"files") >> '[' >> -(file_ % ',') >> ']';
folder_ = '{' >> (
(prop_key(+"id") >> int_ >> commasep) ^
(prop_key(+"parent_id") >> int_ >> commasep) ^
(prop_key(+"path") >> text_ >> commasep)
) >> '}';
file_ = '{' >> (
(prop_key(+"id") >> int_ >> commasep) ^
(prop_key(+"parent_id") >> int_ >> commasep) ^
(prop_key(+"path") >> text_ >> commasep) ^
(prop_key(+"name") >> text_ >> commasep) ^
(prop_key(+"hash") >> text_ >> commasep)
) >> '}';
prop_key = lexeme ['"' >> lazy(_r1) >> '"'] >> ':';
commasep = &char_('}') | ',';
////////////////////////////////////////
// Bonus: properly decoding the string:
text_ = '"' >> *ch_ >> '"';
ch_ = +(
~char_("\"\\")) [ _val += _1 ] |
qi::lit("\x5C") >> ( // \ (reverse solidus)
qi::lit("\x22") [ _val += '"' ] | // " quotation mark U+0022
qi::lit("\x5C") [ _val += '\\' ] | // \ reverse solidus U+005C
qi::lit("\x2F") [ _val += '/' ] | // / solidus U+002F
qi::lit("\x62") [ _val += '\b' ] | // b backspace U+0008
qi::lit("\x66") [ _val += '\f' ] | // f form feed U+000C
qi::lit("\x6E") [ _val += '\n' ] | // n line feed U+000A
qi::lit("\x72") [ _val += '\r' ] | // r carriage return U+000D
qi::lit("\x74") [ _val += '\t' ] | // t tab U+0009
qi::lit("\x75") // uXXXX U+XXXX
>> _4HEXDIG [ append_utf8(qi::_val, qi::_1) ]
);
BOOST_SPIRIT_DEBUG_NODES((files_)(folders_)(file_)(folder_)(start)(text_))
}
private:
qi::rule<It, Data(), Skipper> start;
qi::rule<It, Data::Files(), Skipper> files_;
qi::rule<It, Data::Folders(), Skipper> folders_;
qi::rule<It, Data::File(), Skipper> file_;
qi::rule<It, Data::Folder(), Skipper> folder_;
qi::rule<It, void(const char*), Skipper> prop_key;
qi::rule<It, std::string()> text_, ch_;
qi::rule<It> commasep;
struct append_utf8_f {
template <typename...> struct result { typedef void type; };
template <typename String, typename Codepoint>
void operator()(String& to, Codepoint codepoint) const {
auto out = std::back_inserter(to);
boost::utf8_output_iterator<decltype(out)> convert(out);
*convert++ = codepoint;
}
};
boost::phoenix::function<append_utf8_f> append_utf8;
qi::uint_parser<uint32_t, 16, 4, 4> _4HEXDIG;
};
template <typename Range, typename It = typename boost::range_iterator<Range const>::type>
Data parse(Range const& input) {
grammar<It> g;
It first(boost::begin(input)), last(boost::end(input));
Data parsed;
bool ok = qi::phrase_parse(first, last, g, qi::space, parsed);
if (ok && (first == last))
return parsed;
throw std::runtime_error("Remaining unparsed: '" + std::string(first, last) + "'");
}
}
int main()
{
auto parsed = folder_info::parse(sample);
for (auto& e : parsed.folders)
std::cout << "folder:\t" << e.id << "\t" << e.path << "\n";
for (auto& e : parsed.files)
std::cout << "file:\t" << e.id << "\t" << e.path << "\t" << e.name << "\n";
}
您可以使用boost或json spirit解析器中的属性树。我相信你会在这里找到一些已回答的问题。可能是的重复。谢谢你的回答,但很抱歉我使用它太复杂了,而且我没有成功编译它,最后我决定使用我在顶部发布的“打印”函数的结果,并尝试获取每个文件的“路径”行,然后将ID(编号)替换为文件夹的名称。。。这比将结果放入一个“非常复杂”的数据结构并思考算法要容易得多。思考始终是困难的部分:)无论如何,您没有指定太多(没有代码,没有算法),我只是不推荐“JSON”使用Boost属性树。有趣的是:使用“我的数据结构”打印文本:将向您展示如何使用您在编辑问题时建议的用户友好的数据结构,尽管我建议改为使用。(哦,作为奖励,我实现了字符串(utf8)转义解码)。使用一个完全跳过“复杂”的通用JSON结构的代码,我想我又赢得了这个徽章:)/cc@MooingDuck@Aminos这里是相同的(来自其他答案注释)在没有任何调整的情况下被解析(除了忽略
extract\u from
中的错误)您好,非常感谢您的努力,这非常令人印象深刻,但事实上,在我的JSON中还有关于文件夹或文件对象的其他信息(如创建日期、修改、所有者、大小等),但对我来说最重要的是我在示例中输入的信息(名称、pid、id等),那么,您认为您的代码可以处理这种情况吗?我将等待您的回答,因为我将不得不处理“打印”功能的结果,并尝试提取和放置有关文件或文件夹的信息,例如向量。再次感谢您,祝您度过愉快的一天!哦,好吧。我很惊讶你当时接受了这个答案,因为另一个答案完全相同,只是它接受并忽略了“其他”JSON内容。您是否错过了定义从中提取的更新?它使用完全相同的数据结构——你在问题中提出的数据结构。因此,总而言之,这是一个明显的胜利,因为关注点的分离使所有意外的复杂性都处于危险之中。这些都没有缺点,只能使用typedef。查看类似于该版本中尚未支持的任何c++11构造的页面。@这一个可能不明显:template struct result{typedef void type;}代码>将在c++03中工作
folder: 109 /1/105/110/
folder: 110 /1/105/
file: 26 /1/105/ picture.png
file: 25 /1/105/110/ another_picture.jpg
struct Data {
struct Folder { int id, parent_id; std::string path; };
struct File { int id, parent_id; std::string path, name, md5_hash; };
using Folders = std::vector<Folder>;
using Files = std::vector<File>;
Folders folders;
Files files;
};
start = '{' >>
(folders_ >> commasep) ^
(files_ >> commasep)
>> '}';
folders_ = prop_key(+"folders") >> '[' >> -(folder_ % ',') >> ']';
files_ = prop_key(+"files") >> '[' >> -(file_ % ',') >> ']';
folder_ = '{' >> (
(prop_key(+"id") >> int_ >> commasep) ^
(prop_key(+"parent_id") >> int_ >> commasep) ^
(prop_key(+"path") >> text_ >> commasep)
) >> '}';
file_ = '{' >> (
(prop_key(+"id") >> int_ >> commasep) ^
(prop_key(+"parent_id") >> int_ >> commasep) ^
(prop_key(+"path") >> text_ >> commasep) ^
(prop_key(+"name") >> text_ >> commasep) ^
(prop_key(+"hash") >> text_ >> commasep)
) >> '}';
prop_key = lexeme ['"' >> lazy(_r1) >> '"'] >> ':';
commasep = &char_('}') | ',';
//#define BOOST_SPIRIT_DEBUG
#include <boost/fusion/adapted.hpp>
#include <boost/spirit/include/qi.hpp>
#include <boost/spirit/include/phoenix.hpp>
namespace qi = boost::spirit::qi;
static std::string const sample = R"(
{
"folders" :
[{
"id" : 109,
"parent_id" : 110,
"path" : "\/1\/105\/110\/"
},
{
"id" : 110,
"parent_id" : 105,
"path" : "\/1\/105\/"
}
],
"files" :
[{
"id" : 26,
"parent_id" : 105,
"name" : "picture.png",
"hash" : "md5_hash",
"path" : "\/1\/105\/"
},
{
"id" : 25,
"parent_id" : 110,
"name" : "another_picture.jpg",
"hash" : "md5_hash",
"path" : "\/1\/105\/110\/"
}
]
})";
struct Data {
struct Folder { int id, parent_id; std::string path; };
struct File { int id, parent_id; std::string path, name, md5_hash; };
using Folders = std::vector<Folder>;
using Files = std::vector<File>;
Folders folders;
Files files;
};
BOOST_FUSION_ADAPT_STRUCT(Data::Folder, (int,id)(int,parent_id)(std::string,path))
BOOST_FUSION_ADAPT_STRUCT(Data::File, (int,id)(int,parent_id)(std::string,path)(std::string,name)(std::string,md5_hash))
BOOST_FUSION_ADAPT_STRUCT(Data, (Data::Folders,folders)(Data::Files,files))
namespace folder_info { // adhoc JSON parser
template <typename It, typename Skipper = qi::space_type>
struct grammar : qi::grammar<It, Data(), Skipper>
{
grammar() : grammar::base_type(start) {
using namespace qi;
start = '{' >>
(folders_ >> commasep) ^
(files_ >> commasep)
>> '}';
folders_ = prop_key(+"folders") >> '[' >> -(folder_ % ',') >> ']';
files_ = prop_key(+"files") >> '[' >> -(file_ % ',') >> ']';
folder_ = '{' >> (
(prop_key(+"id") >> int_ >> commasep) ^
(prop_key(+"parent_id") >> int_ >> commasep) ^
(prop_key(+"path") >> text_ >> commasep)
) >> '}';
file_ = '{' >> (
(prop_key(+"id") >> int_ >> commasep) ^
(prop_key(+"parent_id") >> int_ >> commasep) ^
(prop_key(+"path") >> text_ >> commasep) ^
(prop_key(+"name") >> text_ >> commasep) ^
(prop_key(+"hash") >> text_ >> commasep)
) >> '}';
prop_key = lexeme ['"' >> lazy(_r1) >> '"'] >> ':';
commasep = &char_('}') | ',';
////////////////////////////////////////
// Bonus: properly decoding the string:
text_ = '"' >> *ch_ >> '"';
ch_ = +(
~char_("\"\\")) [ _val += _1 ] |
qi::lit("\x5C") >> ( // \ (reverse solidus)
qi::lit("\x22") [ _val += '"' ] | // " quotation mark U+0022
qi::lit("\x5C") [ _val += '\\' ] | // \ reverse solidus U+005C
qi::lit("\x2F") [ _val += '/' ] | // / solidus U+002F
qi::lit("\x62") [ _val += '\b' ] | // b backspace U+0008
qi::lit("\x66") [ _val += '\f' ] | // f form feed U+000C
qi::lit("\x6E") [ _val += '\n' ] | // n line feed U+000A
qi::lit("\x72") [ _val += '\r' ] | // r carriage return U+000D
qi::lit("\x74") [ _val += '\t' ] | // t tab U+0009
qi::lit("\x75") // uXXXX U+XXXX
>> _4HEXDIG [ append_utf8(qi::_val, qi::_1) ]
);
BOOST_SPIRIT_DEBUG_NODES((files_)(folders_)(file_)(folder_)(start)(text_))
}
private:
qi::rule<It, Data(), Skipper> start;
qi::rule<It, Data::Files(), Skipper> files_;
qi::rule<It, Data::Folders(), Skipper> folders_;
qi::rule<It, Data::File(), Skipper> file_;
qi::rule<It, Data::Folder(), Skipper> folder_;
qi::rule<It, void(const char*), Skipper> prop_key;
qi::rule<It, std::string()> text_, ch_;
qi::rule<It> commasep;
struct append_utf8_f {
template <typename...> struct result { typedef void type; };
template <typename String, typename Codepoint>
void operator()(String& to, Codepoint codepoint) const {
auto out = std::back_inserter(to);
boost::utf8_output_iterator<decltype(out)> convert(out);
*convert++ = codepoint;
}
};
boost::phoenix::function<append_utf8_f> append_utf8;
qi::uint_parser<uint32_t, 16, 4, 4> _4HEXDIG;
};
template <typename Range, typename It = typename boost::range_iterator<Range const>::type>
Data parse(Range const& input) {
grammar<It> g;
It first(boost::begin(input)), last(boost::end(input));
Data parsed;
bool ok = qi::phrase_parse(first, last, g, qi::space, parsed);
if (ok && (first == last))
return parsed;
throw std::runtime_error("Remaining unparsed: '" + std::string(first, last) + "'");
}
}
int main()
{
auto parsed = folder_info::parse(sample);
for (auto& e : parsed.folders)
std::cout << "folder:\t" << e.id << "\t" << e.path << "\n";
for (auto& e : parsed.files)
std::cout << "file:\t" << e.id << "\t" << e.path << "\t" << e.name << "\n";
}
folder: 109 /1/105/110/
folder: 110 /1/105/
file: 26 /1/105/ picture.png
file: 25 /1/105/110/ another_picture.jpg