如何在getline上使用分隔符

时间:2016-01-27 03:27:58

标签: c++

我有一个文件,我应该读取每一行并将其存储在数组的结构中,我正在读取文件,但结果是混合的,因为数据是用逗号分隔的,我如何让它读取用逗号分隔的行?

struct Data{
    std::string a;
    std::string b;
    std::string c;
}

int main(){
Data cd[100];

std::ifstream inFile;  
std::string data;
inFile.open("data.txt");                 
if(inFile.good()){                       
    while(getline(inFile, data)){ //when I add, ',' right after data nothing shows up 
        std::stringstream ss(data);     
        ss >>cd[0].a >> cd[0].b >> cd[0].c;
        std::cout <<cd[0].a<<std::endl;
    }
}
else{
std::cout << "can't open" << std::endl;
}

inFile.close();    
//close the file
return 0;
}

3 个答案:

答案 0 :(得分:0)

你在struct之后错过了一个分号,并且在每个循环期间总是覆盖100个结构数组中的第一个结构:最简单的解决方案是创建一个索引计数器,你将随之增加每一行。 要拆分用户定义的分隔符,C ++标准库没有预先制作的功能:要么使用boost,要么滚动自己的解决方案。

即。像这样的东西

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

struct Data {
  std::string a;
  std::string b;
  std::string c;
};

int main() {
  Data cd[100];
  size_t index = 0;
  std::ifstream inFile;
  std::string data;
  inFile.open("data.txt");
  if (inFile.good()) {
    while (getline(inFile, data)) { //when I add, ',' right after data nothing shows up 
      std::string::size_type startPos = data.find_first_not_of(",", 0); /* skip leading delimiters */
      std::string::size_type endPos = data.find_first_of(",", startPos); /* find first real delimiter */
      cd[index].a = data.substr(startPos, endPos - startPos); /* save substring */
      startPos = data.find_first_not_of(",", endPos); /* find next start position */
      endPos = data.find_first_of(",", startPos); /* find next endPosition */
      cd[index].b = data.substr(startPos, endPos - startPos); /* save substring */
      startPos = data.find_first_not_of(",", endPos); /* find next start position */
      endPos = data.find_first_of(",", startPos); /* find next endPosition */
      cd[index].c = data.substr(startPos, endPos - startPos); /* save substring */
      ++index;
    }
  }
  else {
    std::cout << "can't open" << std::endl;
  }
  inFile.close();
  //close the file
  return 0;
}

限制:根据您的结构,它假定您的文件中每行有3个值 - 并且根据您的结构数组,不应超过100行。我建议使用漂亮的std::vector而不是普通的旧C风格数组。

答案 1 :(得分:0)

这可能会在很多情况下帮助你。打开文件并逐行读取文本到std :: string。然后这个有用的实用功能很好用

<强> Utility.h

#ifndef UTILITY_H
#define UTILITY_H

#include <string>

class Utility {
public:    
    static std::vector<std::string> splitString( const std::string& strStringToSplit, const std::string& strDelimiter, const bool keepEmpty = true );

private:
    Utility(); // Private - Not A Class Object
    Utility( const Utility& c ); // Not Implemented
    Utility& operator=( const Utility& c); // Not Implemented
};

#endif // UTILITY_H

<强> Utility.cpp

#include "Utility.h"

std::vector<std::string> Utility::splitString( const std::string& strStringToSplit, const std::string& strDelimiter, const bool keepEmpty ) {

    std::vector<std::string> vResult;
    if ( strDelimeter.empty() ) {
        vResult.push_back( strStringToSplit );
        return vResult;
    }

    std::string::const_iterator itSubStrStart = strStringToSplit.begin(), itSubStrEnd;
    while ( true ) {
        itSubStrEnd = search( itSubStrStart, strStringToSplit.end(), strDelimiter.begin(), strDelimiter.end() );
        std::string strTemp( itSubStrStart, itSubStrEnd );
        if ( keepEmpty || !strTemp.empty() ) {
            vResult.push_back( strTemp );
        }

        if ( itSubStrEnd == strStringToSplit.end() ) {
            break;
        }

        itSubStrStart = itSubStrEnd + strDelimeter.size();
    }

    return vResult;

} // stringToSplit

要在任何类或函数中使用它,您只需包含Utility.h

<强>的main.cpp

#include <iostream>
#include "Utility.h"    

int main() {

    std::string welcome( "Hello,World,How,Are,You,Today,!" );
    std::vector<std::string> result;
    result = Utility::splitString( welcome, std::string( "," ) );

    for ( unsigned u = 0; u < result.size(); u++ ) {
        std::cout << result.at( u ) << std::endl;
    }

    return 0;
}

你的打印出来应该是:

Hello
World
How
Are
You
Today
!

每个单词以逗号分隔,不带空格。您可以使用任何字符作为定义分隔符,它可以是空格,冒号,分号,连字符等。这是一个非常有用且方便的实用程序函数,可以在许多情况下一次又一次地使用;特别是当您尝试解析文件或字符串时。现在你必须要记住这将解析字符串的事实。您仍然必须逐行读取文件并将每一行存储到一个字符串中,然后创建一个字符串向量,用于存储此函数的结果。

首先在类中使用此函数可能看起来不太直观,但我有大约十几个有用的实用程序函数,用于将字符串转换为大写和小写,将字符串转换为不同的基本数据类型,如int,浮动,甚至将用逗号分隔的一串值转换为vec2,vec3&amp; vec4对象,其中所有这些函数在无法实例化的类中声明为静态。它充当容器或包装器,几乎像命名空间。换句话说,我的大多数独立字符串操作函数都会被大量使用,我最终会在这个类中包含它们。

答案 2 :(得分:0)

你回答说你需要用作结构,你仍然可以。有不同的方法。现在根据你的问题和你提供的例子你的struct有3个字符串,你需要它们的数组;让我们假设每行文本都有3个由逗号分隔的字符串值。我创建了一个名为custom.txt的文本文件,这是它的内容:

<强> custom.txt

Hello,world,how
are,you,today

每一行都有三个单词,用逗号分隔,没有空格:现在,我将演示如何使用我在上一个答案中显示的实用函数,在读取一行文本后拆分字符串然后解析它填充你的结构数组。

#include <iostream>
#include <fstream>
#include <tchar.h>
#include <conio.h>
#include "Utility.h"

struct MyStruct {
    std::string str1;
    std::string str2;
    std::string str3;
}; // MyStruct

void parseLine( std::string& strLine, std::vector<std::string>& results, MyStruct& myStruct ) {
    results = Utility::splitString( strLine, std::string( "," ) );
    myStruct.str1 = results[0];
    myStruct.str2 = results[1];
    myStruct.str3 = results[2];
} // parseLine

bool readLine( std::fstream& in, std::string& line ) {
    if ( in.eof() ) {
        return false;
    }
    std::getline( in, line );
    return true;
} // readLine

int main() {
    std::string filename( "custom.txt" );
    std::string strLine;
    sd::fstream fin;

    fin.open( filename.c_str(), std::ios_base::in ) {
    if ( !fin.is_open() ) {
        std::cout << "Error reading in file " << filename << std::endl;
        return -1;
    }

    std::vector<std::string> results;
    // Since I only have 2 lines in this file 
    MyStruct myStruct[2];

    int line = 0;
    while ( readLine( fin, strLine ) ) {
        parseLine( strLine, results, myStruct[line] );
        line++;
    }

    std::cout << "Results: " << std::endl;
    for ( unsigned u = 0; u < 2; u++ ) {
        std::cout << myStruct[u].str1 << " " << myStruct[u].str2 << " " << myStruct[u].str3 << std::endl;
    }
    std::cout << std::endl;

    std::cout << "Press any key to quit." << std::endl;
    _getch();

    return 0;
} // main

关于这个特定实现需要注意的一件事是,循环是根据结构中确切存储的值(3个字符串)确定的,并且数组中的数量必须与行数相匹配保存的文件。这必须提前知道。如果你没有提前知道文件中有多少行,那么我认为你不能使用数组,因为它们在编译时必须是一个常量整数类型。如果您在堆上使用带有指针的动态内存,那么您可以自动化此系统,而无需知道单行上有多少元素,或者不知道特定文件中有多少行。