将字符串拆分为标记并将标记分成两个单独的数组

Posted

技术标签:

【中文标题】将字符串拆分为标记并将标记分成两个单独的数组【英文标题】:Split a string into tokens and separate the tokens into two separate arrays 【发布时间】:2018-10-19 17:14:37 【问题描述】:

我正在尝试创建一个函数 readBooks,它打开一个输入文件流,读取由逗号分隔的书籍和作者列表,文件的每一行都有 1 本书和作者对(例如:Douglas Adams,The银河系漫游指南)。我在如何标记或拆分字符串时遇到问题,以便我可以使用逗号作为分隔符将作者和书名插入两个单独的数组中。任何帮助表示赞赏。

数组的大小由函数中的容量参数定义。数组是在调用 readBooks() 函数之前分配的,因此不需要动态分配它们。

这是我目前的代码:

int readBooks (string filename, string titles[], string authors[], int books, int capacity)
    ifstream file;
    file.open (filename);
    if (file.fail())
        return -1;
    
    else
        int i = 0;
        int j = 0;
        while (i < capacity)
            string line;
            getline (file, line);
            if (line.length() > 0)

            
        
    

【问题讨论】:

getline() 有第三个分隔符参数,您可以使用它来执行此操作。 @Jake,是在调用 readBooks() 之前为数组分配的内存吗? 【参考方案1】:

使用 boost 库会更简单一些,您可以在其中检查多个分隔符。但是,您可以使用 getline() 来搜索行尾分隔符,然后使用 find() 来查找逗号。找到逗号后,您必须确保将其移到标题后面,并剪掉所有空格。

让我知道这是否有意义。

#include <iostream>
#include <fstream>
#include <string>
#include "readBooks.h"

#include <algorithm>
#include <cctype>
#include <locale>

/* trim from start (in place) [Trim functions borrowed from 
 * https://***.com/questions/216823/whats-the-best-way-to-trim-stdstring] 
 */

static inline void ltrim(std::string &s) 
    s.erase(s.begin(), std::find_if(s.begin(), s.end(), [](int ch) 
        return !std::isspace(ch);
    ));


// trim from end (in place)
static inline void rtrim(std::string &s) 
    s.erase(std::find_if(s.rbegin(), s.rend(), [](int ch) 
        return !std::isspace(ch);
    ).base(), s.end());


// trim from both ends (in place)
static inline void trim(std::string &s) 
    ltrim(s);
    rtrim(s);



using namespace std;

int readBooks (string filename, string titles[], string authors[], int books, int capacity)
    ifstream file;
    file.open (filename);
    if (file.fail())
        return -1;
    
    else
        int i = 0;
        string line;

        while(  i < books && i < capacity && getline(file,line) ) 
            // Find the position of the comma, and grab everything before it
            string author(line.begin(), find(line.begin(), line.end(), ','));
            trim(author);
            authors[i] = author;
            // Find position of first character after the ','
            string title(find(line.begin(), line.end(), ',') + 1, line.end());
            trim(title);
            titles[i] = title;
            i++; // increment our index
        
    
    file.close();
    return 0;

这是一个调用它的示例 main()。

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

int main() 

  const int capacity1000;
  const int books3;
  std::string authors[capacity];
  std::string titles[capacity];
  std::string filename"booklist.txt";

  int retval = readBooks(filename, titles, authors, books, capacity);

  return retval;

【讨论】:

我非常喜欢这个带有修剪功能的想法。如果我让书名后跟多个用空格分隔的数字并且有一个逗号分隔书名和数字(例如:book, 1 2 3 4 5 6 ...)和试图将书名和它后面的每个数字分开?我假设它看起来非常相似,但您会更改查找函数的参数? 是的。你可以这样做。但是,一旦在同一行中添加要搜索的第三种类型的项目,切换到正则表达式可能会更好。 find() 解决方案是一个快速而肮脏的解决方案,我们必须进行显式子字符串化。如果你有三个字段,那么正则表达式会更优雅地处理子字符串。【参考方案2】:

首先,如果您甚至不确定输出的大小,为什么还要使用输出数据数组 (std::string[])。 std::vector 总是更好的解决方案。

void readBooks(std::string const& filename, std::vector<std::string> &titles, std::vector<std::string> &authors) 
    std::ifstream file;
    // .....
    // file is opened here
    // ....
    std::string temp;
    while (file) 
        if (!std::getline(file, temp, ','))
            throw std::exception("File is broken?");
        authors.push_back(temp);
        std::getline(file, temp, '\n');
        titles.push_back(temp); //make sure there is no space after ',', as it'd be included in the string.
        //To remove such a space temp.substr(1) can be used.
    

简而言之,它基于std::getline()delimiter参数。

编辑:检查文件以“,”结尾的情况。

【讨论】:

每当你读到东西时,读函数必须检查读是否成功。 @NeilButterworth ,我了解,如果不确定文件的拓扑结构,最好是安全而不是抱歉,但在这种情况下,看起来文件是使用类似结构的程序生成的,所以我不认为会发生比将空行 pushed 到 titles 向量更糟糕的事情。 “所以我不认为会发生比将空行推入标题向量更糟糕的事情” - 但这是错误的行为,预防起来微不足道。 数组的大小应由函数中的输入参数“容量”定义。我也想使用向量,但我想要更多关于数组的经验,并且正在推动自己在我正在编写的这个程序中使用它们。 @Jake,然后不要介意我使用向量,只需将 temp 值添加到您的数组中,而不是 push_backs。

以上是关于将字符串拆分为标记并将标记分成两个单独的数组的主要内容,如果未能解决你的问题,请参考以下文章

c_cpp 根据空间拆分字符串并将每个标记转换为大写。

c_cpp 根据空间拆分字符串并将每个标记转换为大写。

拆分一个字符串并将其放入两个数组中

strtok() 如何将字符串拆分为 C 中的标记?

将字符串拆分为标记并在 Perl 中存储分隔符

如何将字符串拆分为多个部分?