Запись массива char на основе инструкций из файла - PullRequest
1 голос
/ 28 ноября 2011

Мне нужно заполнить массив символов, чтобы он выглядел так:

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?

Моя проблема, кажется, происходит в последней строке.Компилятор не выдаёт мне ошибок, но когда я запускаю программу, она вызывает необработанное исключение и указывает на эту строку.

Как я могу это исправить?

Ответы [ 5 ]

3 голосов
/ 28 ноября 2011

Мне нужно заполнить массив символов, чтобы он выглядел так:

Нет, нет. Это 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.

2 голосов
/ 28 ноября 2011

По сути, ваша программа падает, потому что вы никогда не выделяете память для своих массивов символов. Более того, 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::string и std::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);
}
1 голос
/ 28 ноября 2011

Обязательный ответ на основе Boost Spirit:

Основной:

typedef std::map<size_t, std::string> Result;

// ...
const std::string input = // TODO read from file :)
    "TEXDEF 1 Resources/Textures/tex2.png\n"
    "TEXDEF 0 Resources/Textures/tex.png\n"
    "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.png\n"
        "TEXDEF 0 Resources/Textures/tex.png\n"
        "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;
    }
}
0 голосов
/ 28 ноября 2011

Попробуйте использовать [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 учебник .


Ресурсы

Базовый ввод / вывод файла
std::map

0 голосов
/ 28 ноября 2011

При чтении в строку необходимо подготовить буфер и передать указатель на этот буфер в sscanf_s.Передача адреса переменной char * не будет работать.Кроме того, вы должны использовать форму с ограниченной длиной, например, %255s для 256-байтового буфера.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...