使用strtok()来解析文本文件

时间:2013-04-30 01:52:49

标签: c++ file parsing substr strtok

我一直在努力创建一个解析文本文件的程序,并将6条信息提供给一个对象数组。对我来说问题是我在解决如何处理文本文件时遇到了问题。有人告诉我,我需要做的第一步是编写一些代码来计算每个条目的字母长度。 txt 文件采用以下格式:

"thing1","thing2","thing3","thing4","thing5","thing6"

这是我的代码的当前版本:

#include<iostream>
#include<string>
#include<fstream>
#include<cstring>

using namespace std;

int main()
{
ifstream myFile("Book List.txt");

while(myFile.good())
{
    string line;

    getline(myFile, line);

    char *sArr = new char[line.length() + 1];
    strcpy(sArr, line.c_str());

    char *sPtr;

    sPtr = strtok(sArr, " ");

    while(sPtr != NULL)
    {
        cout << strlen(sPtr) << " ";
        sPtr = strtok(NULL, " ");
    }
    cout  << endl;
}
myFile.close();
return 0;
}

所以有两件事让我现在很难。

1)我如何处理分隔符?

2)我如何处理“跳过”每行中的第一个引号?

3 个答案:

答案 0 :(得分:1)

读入字符串而不是c样式的字符串。这意味着您可以使用方便的std方法。

std::string::find()方法可以帮助您找到要解析的每个内容。

http://www.cplusplus.com/reference/string/string/find/

您可以使用它来查找所有逗号,这将为您提供所有内容的开头。

然后您可以使用std::string::substr()将字符串剪切成每个部分。

http://www.cplusplus.com/reference/string/string/substr/

你可以设法通过传入1个以上的开头和1个小于事物的长度来摆脱引号,你也可以使用

答案 1 :(得分:1)

如果您必须使用strtok,则此代码段应足以修改您的程序以解析您的数据:

#include <cstdio>
#include <cstring>

int main ()
{
  char str[] ="\"thing1\",\"thing2\",\"thing3\",\"thing4\",\"thing5\"";
  char * pch;
  printf ("Splitting string \"%s\" into tokens:\n",str);
  pch = strtok (str,"\",");
  while (pch != NULL)
  {
    printf ("%s\n",pch);
    pch = strtok (NULL, ",\"");
  }
  return 0;
}

如果您不必使用strtok,那么您应该像其他人所建议的那样使用std::string。使用std::stringstd::istringstream

#include <string>
#include <sstream>
#include <vector>
#include <iostream>

int main ()
{
  std::string str2( "\"thing1\",\"thing2\",\"thing3\",\"thing4\",\"thing5\"" ) ;

  std::istringstream is(str2);
  std::string part;

  while (getline(is, part, ','))
     std::cout << part.substr(1,part.length()-2) << std::endl;

  return 0;
}

答案 2 :(得分:1)

对于初学者,如果可以避免,请不要使用strtok(并且您可以轻松地在此处 - 甚至可以避免使用find系列功能。

如果你想阅读整行,然后解析它:

#include <algorithm>
#include <iostream>
#include <iterator>
#include <sstream>
#include <string>
#include <vector>

// defines a new ctype that treats commas as whitespace
struct csv_reader : std::ctype<char>
{
    csv_reader() : std::ctype<char>(get_table()) {}

    static std::ctype_base::mask const* get_table()
    {
        static std::vector<std::ctype_base::mask> rc(table_size, std::ctype_base::mask());
        rc['\n'] = std::ctype_base::space;
        rc[','] = std::ctype_base::space;
        return &rc[0];
    }
};

int main()
{
    std::ifstream fin("yourFile.txt");
    std::string line;
    csv_reader csv;
    std::vector<std::vector<std::string>> values;
    while (std::getline(fin, line))
    {
        istringstream iss(line);
        iss.imbue(std::locale(std::locale(), csv));
        std::vector<std::string> vec;
        std::copy(std::istream_iterator<std::string>(iss), std::istream_iterator<std::string>(), std::back_inserter(vec));
        values.push_back(vec);
    }
    // values now contains a vector for each line that has the strings split by their commas
    fin.close();
    return 0;
}

这回答了你的第一个问题。对于您的第二个,您可以通过将所有引号添加到rc掩码(也将它们视为空格)来跳过所有引号,或者之后可以将它们删除(直接或使用transform):

std::transform(vec.begin(), vec.end(), vec.begin(), [](std::string& s)
{
    std::string::iterator pend = std::remove_if(s.begin(), s.end(), [](char c)
    {
        return c == '"';
    });
    s.erase(pend, s.end());
});