我正在尝试调试我的C ++程序,因为我逐个构建它。现在,我试图做的就是测试我将html文件读入string
的过程(顺便说一句,它是否会像读取文本文件一样直接工作?它是什么?基本上是一样的,对吧?),并且我得到的错误引用了我的类实现的不同部分,即使在我的代码运行中根本没有使用该实现。我的编译器是Mac OSX Yosemite上的XCode 6,配备Intel Core i5的Macbook Pro。在我真正尝试编译之前,它没有标记任何错误。
错误:
Undefined symbols for architecture x86_64:
"HtmlProcessor::HtmlProcessor()", referenced from:
_main in main.o
"HtmlProcessor::~HtmlProcessor()", referenced from:
_main in main.o
"DocTree::_hp", referenced from:
DocTree::setTree(std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const&) in CSS_Generator.o
ld: symbol(s) not found for architecture x86_64
clang: error: linker command failed with exit code 1 (use -v to see invocation)
由于我不允许在此发布内嵌图片,请查看此链接,完整错误:http://i.stack.imgur.com/1BEqn.png
如果没有完成&#34;代码转储&#34;,我会发布我的文件的缩写版本,包括似乎相关的部分。
main.cpp中:
#include <iostream>
#include "CSS_Generator.h"
int main(int argc, const char * argv[]) {
node dummyNode;
HtmlProcessor HP;
HP.processFile("sample.html", dummyNode);
return 0;
}
CSS_Generator.h
#ifndef __CSS_Template_Generator__CSS_Generator__
#define __CSS_Template_Generator__CSS_Generator__
#include <stdio.h>
#include <string>
#include <vector>
#include <utility>
#include <set>
struct node
{
std::string element_type;
std::set<std::string> class_list;
std::string iden;
std::vector<node*> children;
};
class HtmlProcessor
{
public:
HtmlProcessor();
~HtmlProcessor();
void processFile(const std::string &, node &);
private:
bool _getNextBracket(std::pair<std::string::const_iterator &, const std::string::const_iterator &> &, bool &);
bool _processTag(std::string::const_iterator, const std::string::const_iterator, node &);
bool _hasNextAttribute(std::string::const_iterator &, const std::string::const_iterator &, std::pair<std::string, std::string> &);
bool _processAttributes(const std::vector<std::pair<std::string, std::string>> &, std::set<std::string> &, std::string &);
std::vector<std::string> _splitWithoutQuotes(std::string::const_iterator, std::string::const_iterator);
bool _splitAttribute(const std::string &, std::pair<std::string, std::string> &);
static const std::string _elementTypeChars;
static const std::string _attributeTypeChars;
static const std::string _attributeValChars;
};
class DocTree
{
public:
DocTree();
~DocTree();
void setTree(const std::string &);
void getCSS(const std::string &);
private:
node * _root;
void _destroyTree(node *);
std::string _tree2Str(node *);
std::string _innerTree2Str(node *, unsigned);
static HtmlProcessor _hp;
};
#endif /* defined(__CSS_Template_Generator__CSS_Generator__) */
CSS_Generator.cpp:
#include "CSS_Generator.h"
#include <iostream>
#include <sstream>
#include <string>
#include <vector>
#include <algorithm>
#include <utility>
#include <fstream>
/* -------------------------- DocTree class implementation begin ---------------------------*/
void DocTree::setTree(const std::string & F)
{
_destroyTree(_root);
try
{
_hp.processFile(F, *_root);
}
catch (const std::string & error_message)
{
std::cout << error_message << std::endl;
}
}
void DocTree::_destroyTree(node * rt)
{
for (std::vector<node*>::iterator it(rt->children.begin()), offend(rt->children.end()); it != offend; ++it)
_destroyTree(*it);
delete rt;
}
std::string DocTree::_innerTree2Str(node * rt, unsigned depth)
{
std::string css("");
if (depth > 0)
css.append(" > ");
if (rt)
{
css.append(rt->element_type);
if (!rt->class_list.empty())
{
for (std::set<std::string>::iterator it(rt->class_list.begin()), offend(rt->class_list.end()); it != offend; ++it)
css.append("." + *it);
}
if (!rt->iden.empty())
{
css.append("#" + rt->iden);
}
if (!rt->children.empty())
{
std::string tabs(depth, '\t');
for (std::vector<node*>::iterator it(rt->children.begin()), offend(rt->children.end()); it != offend; ++it)
css.append("\n" + tabs + _innerTree2Str(*it, depth + 1));
}
}
return css;
}
std::string DocTree::_tree2Str(node * rt)
{
return _innerTree2Str(rt, 0);
}
/* -------------------------- DocTree class implementation end -------------------------------*/
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
/* ---------------------- HtmlProcessor class implementation begin -------------------------- */
const std::string HtmlProcessor::_elementTypeChars = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";
const std::string HtmlProcessor::_attributeTypeChars = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ-_";
const std::string HtmlProcessor::_attributeValChars = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ-_ ";
void HtmlProcessor::processFile(const std::string & F, node & nd)
{
/* F: Path of HTML file
nd: node element that will be the root of the document tree tree if processing is successful
Throws an error if any problems occur during the processing
*/
std::ifstream html_file(F);
std::string html_str, line;
while(std::getline(html_file, line)) html_str.append(line);
/* ----- TEST ----- */
std::cout << html_str;
/* ---------------- */
}
bool HtmlProcessor::_processTag(std::string::const_iterator it1, const std::string::const_iterator it2, node & nd)
{
/*
[it1, it2): iterators for the range of the string
nd: node in which classes and ids of the tage are stored
Returns true or false depending on whether a problem was encountered during the processing.
*/
/* Get the element type, at the beginning of the tag: */
std::string elementType("");
while (_elementTypeChars.find(*it1) != std::string::npos && it1 != it2) elementType.push_back(*it1++);
if (elementType.empty()) return false;
nd.element_type = elementType;
/* Get any attributes: */
std::vector<std::pair<std::string, std::string>> attributes;
std::pair<std::string, std::string> thisAttribute;
while (_hasNextAttribute(it1, it2, thisAttribute)) attributes.push_back(thisAttribute);
if (!_processAttributes(attributes, nd.class_list, nd.iden)) return false;
return true;
}
bool HtmlProcessor::_hasNextAttribute(std::string::const_iterator & it1, const std::string::const_iterator & it2, std::pair<std::string, std::string> & attrHolder)
{
/* Parses the first HTML attributes in the iterator range [it1, it2), adding them to attrHolder; eg.
class="myClass1 myClass2" id="myId" onsubmit = "myFunction()"
---------- _hasNextAttribute -------->
attrHolder = (class, myClass1 myClass2)
When the function terminates, it1 will be the iterator to the last character parsed, will be equal to
it2 if no characters were parsed.
*/
while (*it1 == ' ' && it1 != it2) ++it1; /* Skip through left whitespace padding */
if (it1 == it2) return true; /* No attributes in tag; only whitespace after the element name. Such is valid HTML. */
std::string attr(""); /* String to hold the attribute type, expected after any whitespace. Should be non-empty. */
while (_attributeTypeChars.find(*it1) == std::string::npos && it1 != it2) attr.push_back(*it1++);
if (attr.empty()) return false;
while (*it1 == ' ' && it1 != it2) ++it1; /* Skip through whitespace padding between the attribute name and equals sign */
if (*it1 != '=' || it1++ == it2) return false; /* Current character should be an equals sign */
while (*it1 == ' ' && it1 != it2) ++it1; /* Skip through whitespace between the equals sign and quotation mark */
if (*it1 != '"' || it1++ == it2) return false; /* Current character should be a quotation mark */
std::string val(""); /* String to hold the attribute's value, exepcted after the first quotation mark. */
while (_attributeValChars.find(*it1) != std::string::npos) val.push_back(*it1++);
if (attr.empty()) return false;
if (*it1 != '"' || it1++ != it2) return false; /* Current character should be a quotation mark */
/* If we're here, it1 should point to the character after the quotation mark that closes off the attribute's value */
attrHolder = std::make_pair(attr, val);
return true;
}
bool HtmlProcessor::_processAttributes(const std::vector<std::pair<std::string, std::string>> & attrs, std::set<std::string> &classesTarget, std::string & identifierTarget)
{
for (std::vector<std::pair<std::string, std::string>>::const_iterator it(attrs.cbegin()), offend(attrs.end()); it != offend; ++it)
{
std::string thisAttr(it->first), thisVal(it->second);
std::transform(thisAttr.begin(), thisAttr.end(), thisAttr.begin(), ::tolower);
if (thisAttr == "id")
identifierTarget = thisVal;
else if (thisAttr == "class")
{
/* Since the value for a class attribute can be several classes separated by whitespace,
add all of them to set of classes for the node.
*/
std::stringstream ss(thisAttr);
std::string thisClass;
while (std::getline(ss, thisClass, ' ')) classesTarget.insert(thisClass);
}
}
return true;
}
bool HtmlProcessor::_getNextBracket(std::pair<std::string::const_iterator &, const std::string::const_iterator &> & bounds, bool & IQ)
{
bool foundOne = false;
while (bounds.first != bounds.second)
{
switch (*bounds.first)
{
case '<':
if (!IQ) foundOne = true;
break;
case '>':
if (!IQ) foundOne = true;
break;
case '"':
IQ = !IQ;
break;
}
if (foundOne) break;
++bounds.first;
}
return foundOne;
}
/* ----------------------------- HtmlProcessor class implementation end --------------------------- */