根据文件中的指令写入字符数组

Writing a char array based on instructions from a file

本文关键字:字符 数组 指令 文件      更新时间:2023-10-16

我需要填充一个字符数组,使它看起来像这样:

char* str[3] = {"tex.png", "text2.png", "tex3.png"};`

我需要从一个像这样的文件中加载所有这些内容:

TEXDEF 0 Resources/Textures/tex.png
TEXDEF 1 Resources/Textures/tex2.png
TEXDEF 2 Resources/Textures/tex3.png

到目前为止,我有:

char* texs[3];
char* tstr;
int tnum;
sscanf_s(oneline, "TEXDEF %d %s", &tnum, &tstr);
texs[tnum] = tstr;  // Problem?

我的问题出现在最后一行。编译器没有给我任何错误,但是当我运行程序时,它会导致一个未处理的异常,并指向那一行。

我该如何解决这个问题?

我需要填充一个字符数组,使它看起来像这样:

不,你没有。这是c++,所以使用std::string

std::array<std::string, 3> filenames;
std::ifstream infile("thefile.txt");
std::string line;
for (unsigned int i = 0; std::getline(infile, line); ++i)
{
  std::string tok1, tok3;
  unsigned int tok2;
  if (!(infile >> tok1 >> tok2 >> tok3)) { /* input error! */ }
  if (tok1 != "TEXDEF" || tok2 != i || i > 2) { /* format error */ }
  filenames[i] = tok3;
}

如果文件名的数量是可变的,你可以用std::vector<std::string>替换数组,而忽略i > 2范围检查

基本上,您的程序正在崩溃,因为您从未为您的字符数组分配内存。此外,sscanf_s()期望格式字符串中的每个%s标记有两个参数。它需要一个指向缓冲区的指针和缓冲区的大小(这是为了避免缓冲区溢出)。

你必须传递一个指针一个字符数组给sscanf_s(),像:

char tstr[3][MAX_PATH]; // replace `MAX_PATH` by proper maximum size.
sscanf_s(oneline, "TEXDEF %d %s", &tnum, tstr[0], MAX_PATH);
sscanf_s(oneline, "TEXDEF %d %s", &tnum, tstr[1], MAX_PATH);
sscanf_s(oneline, "TEXDEF %d %s", &tnum, tstr[2], MAX_PATH);

然而,手工管理这确实很痛苦。使用std::vector<>std::stringstd::ifstream可能会容易得多,因为内存将被自动管理。

std::ifstream file("path/to/file.txt");
std::vector<std::string> inputs;
// assuming one entry on each line.
for (std::string line; std::getline(file,line); )
{
    // extract components on the line.
    std::istringstream input(line);
    std::string texdef;
    int index = 0;
    std::string path;
    input >> texdef;
    input >> index;
    std::getline(input, path);
    // if the results may appear out of order,
    // insert at `index` instead of at the end.
    inputs.push_back(path);
}

基于Boost Spirit的必选答案:

主:

typedef std::map<size_t, std::string> Result;
// ...
const std::string input = // TODO read from file :)
    "TEXDEF 1 Resources/Textures/tex2.pngn"
    "TEXDEF 0 Resources/Textures/tex.pngn"
    "TEXDEF 2 Resources/Textures/tex3.png";
Result result;
if (doTest(input, result))
{
    std::cout << "Parse results: " << std::endl;
    for (Result::const_iterator it = result.begin(); it!= result.end(); ++it)
        std::cout << "Mapped: " << it->first << " to " << it->second << std::endl;
}
输出:

Parse results: 
Mapped: 0 to Resources/Textures/tex.png
Mapped: 1 to Resources/Textures/tex2.png
Mapped: 2 to Resources/Textures/tex3.png

完整代码:

#include <string>
#include <map>
#include <boost/spirit/include/qi.hpp>
#include <boost/fusion/include/std_pair.hpp>
namespace qi = boost::spirit::qi;
typedef std::map<size_t, std::string> Result;
template <typename Iterator, typename Skipper> struct TexDefs
  : qi::grammar<Iterator, Result(), Skipper>
{
    TexDefs()
      : TexDefs::base_type(texdefs)
    {
        texdefs  = def >> *(qi::eol >> def);
        def      = "TEXDEF" >> key >> filename;
        key      = qi::uint_;
        filename = qi::lexeme [ +(qi::char_ - qi::eol) ];
    }
    typedef Result::key_type    key_t;
    typedef Result::mapped_type value_t;
    qi::rule<Iterator, Result(), Skipper>        texdefs;
    qi::rule<Iterator, std::pair<key_t, value_t>(), Skipper> def;
    qi::rule<Iterator, key_t(), Skipper>         key;
    qi::rule<Iterator, value_t(), Skipper>       filename;
};
template <typename Input, typename Skip>
   bool doTest(const Input& input, Result& into, const Skip& skip)
{
    typedef typename Input::const_iterator It;
    It first(input.begin()), last(input.end());
    TexDefs<It, Skip> parser;
    bool ok = qi::phrase_parse(first, last, parser, skip, into);
    if (!ok)         std::cerr << "Parse failed at '" << std::string(first, last) << "'" << std::endl;
    if (first!=last) std::cerr << "Warning: remaining unparsed input: '" << std::string(first, last) << "'" << std::endl;
    return ok;
}
template <typename Input>
bool doTest(const Input& input, Result& into)
{
    // allow whitespace characters :)
    return doTest(input, into, qi::char_(" t"));
}
int main(int argc, const char *argv[])
{
    const std::string input = // TODO read from file :)
        "TEXDEF 1 Resources/Textures/tex2.pngn"
        "TEXDEF 0 Resources/Textures/tex.pngn"
        "TEXDEF 2 Resources/Textures/tex3.png";
    Result result;
    if (doTest(input, result))
    {
        std::cout << "Parse results: " << std::endl;
        for (Result::const_iterator it = result.begin(); it!= result.end(); ++it)
            std::cout << "Mapped: " << it->first << " to " << it->second << std::endl;
    }
}

读入字符串时,必须准备一个缓冲区,并将指向该缓冲区的指针传递给sscanf_s。传递char *变量的地址将不起作用。此外,您应该使用长度限制的形式,例如,对于256字节的缓冲区,%255s

尝试使用[basic] c++:

std::ifstream myfile ("example.txt");
std::vector<std::string> lines;
if(myfile.is_open())
{
    for(std::string line; std::getline(myfile, line); )
    {
        lines.push_back(line);
    }
    myfile.close();
}
else
{
    cout << "Unable to open file";
}
要解析和打印数据,只需像数组一样遍历lines:
std::map<int, std::string> data;
for(size_t i = 0; i < lines.length(); ++i)
{
    int n;
    std::string s;
    std::stringstream ss;
    ss << lines[i].substr(((static std::string const)"TEXDEF ").length());
    ss >> n >> s;
    data[n] = s;
    // Print.
    std::cout << lines[i] << "n";
}
std::cout << std::endl;

我建议你阅读这个std::map教程。


基本文件I/O
std::map