【问题标题】:Is there any easy way to read a line from a file, split the first text part into a string, then split the last number part into a float?有没有简单的方法从文件中读取一行,将第一个文本部分拆分为一个字符串,然后将最后一个数字部分拆分为一个浮点数?
【发布时间】:2022-01-21 19:49:50
【问题描述】:

我有一个问题,我一直无法找到解决的好方法,主要是因为我对 C++ 比较陌生,但对编程并不陌生。我有一个包含几行的文件,其中之一是:

Plain Egg 1.45

我需要能够读取该行并将第一部分“Plain Egg”拆分为一个字符串,然后将最后一部分 1.45 拆分为一个浮点数,然后对其余行执行此操作。以下是我到目前为止的一些代码,但由于某种原因它甚至拒绝读取文件时遇到了问题:

string line;
ifstream menuFile("menu.txt");
if (menuFile.is_open())
{
    int i = 0;
    while (getline(menuFile, line));
    {
        cout << line << endl;

        istringstream iss(line);

        iss >> dataList[i].menuItem >> dataList[i].menuPrice;
        /*iss >> dataList[i].menuPrice;*/
        i++;
    }

}
else
{
    cout << "Unable to open file.";
}

当我运行它时,它不会吐出“无法打开文件。”,当我跟踪它时,它确实进入了 if 循环,但它只是不读取它。除了这个问题,我想知道这段代码是否会按照我想要的方式工作,如果没有,如何解决这个问题。

编辑:当我运行它时,它会输出文件最后一行所说的内容,即“Tea 0.75”。完整文件如下:

Plain Egg 1.45
Bacon and Egg 2.45
Muffin 0.99
French Toast 1.99
Fruit Basket 2.49
Cereal 0.69
Coffee 0.50 
Tea 0.75

编辑2:出于某种原因,以下代码直接进入最后一行,Tea 0.75,我不知道为什么,getline 不应该逐行直到最后一行(?):

string line;
int index;
ifstream menuFile("menu.txt");
if (menuFile.is_open())
{
    while (getline(menuFile, line));
    {
        cout << line << endl;
        index = line.find_last_of(' ');
        cout << index << endl;
    }

}

编辑 3:上面的代码在 while 循环的末尾有一个分号,难怪它只是在最后一行结束,呃。

【问题讨论】:

  • 因为协议使用空格作为分隔符,并且还允许标记中的空格,所以您需要花费额外的精力来查找不变的部分并以它们开头。在这种情况下,最后一个标记将始终是一个浮点数。从行尾开始,向后解析浮点数。其余的将是字符串。
  • 你可以用一个非常直接的 std::regex,或者一个不太难的自定义解析例程,或者一个非常简单的 Boost Spirit X3 语法来解决这个问题。取决于您的舒适程度和需求。
  • 这是一个很棒的建议,我现在要搜索它,但是从行尾读到后面的最佳方法是什么?
  • @BrentMayes:是的,您使用getline 的方式很好。您只需处理getline 成功后执行的代码。
  • @user4581301 std::string::rfind() 在这种情况下比std::string::find_last_of() 更合适。

标签: c++ sstream


【解决方案1】:
  • 将线抓成字符串。
  • 获取最后一个分隔符的位置。
  • 您的文本是直到分隔符位置的行的子字符串。
  • 您的号码是从分隔符位置开始的行的子字符串。您需要先将其转换为双精度(并且您应该检查错误)。

[Demo]

#include <iostream>  // cout
#include <string>  // find_last_of, getline, stod

int main()
{
    std::string line{};
    while (std::getline(std::cin, line))
    {
        auto pos{line.find_last_of(' ')};
        auto text{line.substr(0, pos)};
        auto number{std::stod(line.substr(pos))};
        std::cout << "text = " << text << ", number = " << number << "\n";
    }
}

// Outputs
//
//   text = Plain Egg, number = 1.45
//   text = Bacon and Egg, number = 2.45
//   text = Muffin, number = 0.99
//   text = French Toast, number = 1.99
//   text = Fruit Basket, number = 2.49
//   text = Cereal, number = 0.69
//   text = Coffee, number = 0.5
//   text = Tea, number = 0.75
//   

考虑到@Dúthomhas 的 cmets 的更强大的解决方案:

  • 在找到最后一个分隔符之前修剪字符串的右侧。
  • 捕获std::stod 异常。

此解决方案检测到:

  • 空白行。
  • 没有文字的行。
  • 没有数字的行。
  • 数字格式不正确。

[Demo]

#include <boost/algorithm/string.hpp>
#include <fmt/core.h>
#include <iostream>  // cout
#include <string>  // find_last_of, getline, stod

int main()
{
    std::string line{};
    while (std::getline(std::cin, line))
    {
        try
        {
            boost::trim_right(line);
            auto pos{line.find_last_of(' ')};
            auto text{line.substr(0, pos)};
            auto number{std::stod(line.substr(pos))};
            std::cout << "text = " << text << ", number = " << number << "\n";
        }
        catch (const std::exception&)
        {
            std::cout << fmt::format("* Error: invalid line '{}'\n", line);
        }
    }
}

// Outputs:
//
//   text = Plain Egg, number = 1.45
//   text = Bacon and Egg, number = 2.45
//   text = Muffin, number = 0.99
//   text = French Toast, number = 1.99
//   * Error: invalid line ''
//   * Error: invalid line 'Fruit Basket'
//   * Error: invalid line '0.75'
//   * Error: invalid line 'Coffee blah'

【讨论】:

  • 你打败了我。在找到pos 之前,请确保line 没有任何尾随空格。 (并且考虑空行和带有单个项目的行的可能性。)
  • 在看到这个之前我刚刚完成了我的代码,我做了几乎同样的事情,谢谢你和所有帮助我的人!
  • “培根和鸡蛋 2.45”应该是“培根和2 hard boiled eggs 2.45”。 ?
  • 3个煮鸡蛋! :D
【解决方案2】:

您可以对给定的输入执行此操作,方法是迭代字符串以查找浮点值开始的索引。然后抓取每个部分的子字符串并将值转换为浮点数。

#include <iostream>

using namespace std;

int main()
{
    string s = "Bacon and Egg 2.45";
    int l = 0;
    string food;
    float val = 0.0;
    
    for (int i = 0; i < s.length(); i++)
    {
        if(isdigit(s[i]))
        {
            l = i;
            break;
        }
    }
    
    food = s.substr(0,l);
    val = stof(s.substr(l,s.length()));
    cout << food << endl;
    cout << val;
}

【讨论】:

  • 函数调用isdigit(s[i]) 将导致大多数无法表示为unsigned char 的函数参数的未定义行为(即对于负值)。有关更多信息和修复,请参阅 this answer to another question
  • 我正要利用 string_last_of 使用这个方法,这似乎是一个很好的方法。
  • 这适用于"Bacon and 3 Eggs 2.45" 吗? :)
  • 不,它不会,幸运的是我唯一的回应是列出的,但出于这个原因,我使用 find_last_of 并找到最后一个空格。
  • @BrentMayes 没错。您在那条线上唯一不变的是 last 项目始终是数字。因此,找到最后一项的开头并在那里拆分行。
【解决方案3】:

有点笨拙:使用寻找字符串中最后一个空格的相同想法,但左右修剪空格。不好的是,虽然它有很多代码,但它仍然无法与任何形式的 unicode 一起使用。

  // Right trim the line                                                                                                                                                                                                                                     
  while(!line.empty()) {
    if (isspace(line.back())) {
      line.pop_back();
    }
  }

  // Find the last space                                                                                                                                                                                                                                     
  size_t pos = line.find_last_of(" \t");
  if (pos == std::string::npos) {
    // Bad line: no spaces or only spaces                                                                                                                                                                                                                    
    handle_it();
  }

  // Get the price                                                                                                                                                                                                                                           
  double price = 0.0;
  try {
    size_t end_pos = 0;
    price = std::stod(line.substr(pos), &end_pos);
    if ((pos + end_pos) != line.length()) {
        // Another bad format: garbage at the end                                                                                                                                                                                                            
        handle_it();
      }
  } catch (...) {
    // Another bad format                                                                                                                                                                                                                                    
    handle_it();
  }

  // Left trim the item                                                                                                                                                                                                                                      
  size_t skip = 0;
  while(skip > pos && isspace(line[skip])) {
    skip++;
  }
  if (skip == pos) {
    // Another bad format: spaces + price                                                                                                                                                                                                                    
    handle_it();
  }

  // Right trim the item                                                                                                                                                                                                                                     
  // we know that we have at leas one non-space                                                                                                                                                                                                              
  pos--;
  while(isspace(line[pos])) {
    pos--;
  }

  std::string item = line.substr(skip, pos + 1);

【讨论】:

  • 使用不能表示为 unsigned char 的值(例如,使用字符代码 >127 时为负值)并且不是 EOF 调用 std::isspace 将导致未定义的行为。有关更多信息以及如何修复它,请参阅this answer to another question。有关std::isdigit 的信息也适用于std::isspace
猜你喜欢
  • 2022-06-11
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2017-04-23
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多