【问题标题】:Reading integers from a text file with words从带有单词的文本文件中读取整数
【发布时间】:2010-01-18 06:34:54
【问题描述】:

我正在尝试从这样的文本文件中读取整数......

ALS 46000
BZK 39850
CAR 38000
//....

使用 ifstream。

我考虑了 2 个选项。

1) 使用 Boost 的正则表达式

2) 创建一个一次性字符串(即我读一个单词,不做任何事情,然后读分数)。但是,这是最后的手段。

有什么方法可以在 C++ 中表达我希望 ifstream 只读取整数文本?如果事实证明有更简单的方法可以实现这一点,我不愿意使用正则表达式。

【问题讨论】:

    标签: c++ file


    【解决方案1】:

    为什么要把简单的事情复杂化?

    这有什么问题:

    ifstream ss("C:\\test.txt");
    
    int score;
    string name;
    while( ss >> name >> score )
    {
        // do something with score
    }
    

    【讨论】:

    • 不幸的是,这是一个典型的反模式。 EOF 测试(正如您的 while(ss) 所暗示的那样)在您读取文件末尾之后的“之后”之前不会返回 true。因此,当没有输入时,“做某事”将使用无效值做某事。 Prefer 'while ( ss >> name >> score)' 所以只有读取成功才会进入循环。
    【解决方案2】:

    编辑: 事实上,possible to work on streams directly 比我之前建议的要精神,带有解析器:

    +(omit[+(alpha|blank)] >> int_)
    

    还有一行代码(变量定义除外):

    void extract_file()
    {
        std::ifstream f("E:/dd/dd.trunk/sandbox/text.txt");    
        boost::spirit::istream_iterator it_begin(f), it_end;
    
        // extract all numbers into a vector
        std::vector<int> vi;
        parse(it_begin, it_end, +(omit[+(alpha|blank)] >> int_), vi);
    
        // print them to verify
        std::copy(vi.begin(), vi.end(), 
            std::ostream_iterator<int>(std::cout, ", " ));
    
    }
    

    你可以用一行一次将所有数字变成一个向量,再简单不过了。


    如果您不介意使用boost.spirit2。仅从一行中获取数字的解析器是

    omit[+(alpha|blank)] >> int_
    

    提取一切是

    +(alpha|blank) >> int_
    

    查看下面的整个程序(使用 VC10 Beta 2 测试):

    #include <boost/spirit/include/qi.hpp>  
    #include <iostream>  
    #include <string>  
    #include <cstring> 
    #include <vector>  
    
    #include <fstream>
    #include <algorithm>
    #include <iterator>
    
    using std::cout; 
    
    using namespace boost::spirit;  
    using namespace boost::spirit::qi;    
    
    void extract_everything(std::string& line) 
    {
        std::string::iterator it_begin = line.begin();
        std::string::iterator it_end   = line.end();    
    
        std::string s;
        int i;
    
        parse(it_begin, it_end, +(alpha|blank)>>int_, s, i);
    
        cout << "string " << s  
             << "followed by nubmer " << i 
             << std::endl;
    
    }
    
    void extract_number(std::string& line) 
    {
        std::string::iterator it_begin = line.begin();
        std::string::iterator it_end   = line.end();    
    
        int i;
    
        parse(it_begin, it_end, omit[+(alpha|blank)] >> int_, i);
    
        cout << "number only: " << i << std::endl;
    
    } 
    
    void extract_line()
    {
        std::ifstream f("E:/dd/dd.trunk/sandbox/text.txt");
        std::string s;
        int i; 
    
        // iterated file line by line
        while(getline(f, s))
        {
            cout << "parsing " << s << " yields:\n";
            extract_number(s);  // 
            extract_everything(s);
        }
    
    }
    
    void extract_file()
    {
        std::ifstream f("E:/dd/dd.trunk/sandbox/text.txt");    
        boost::spirit::istream_iterator it_begin(f), it_end;
    
        // extract all numbers into a vector
        std::vector<int> vi;
        parse(it_begin, it_end, +(omit[+(alpha|blank)] >> int_), vi);
    
        // print them to verify
        std::copy(vi.begin(), vi.end(), 
            std::ostream_iterator<int>(std::cout, ", " ));
    
    }
    
    int main(int argc, char * argv[])  
    {    
        extract_line();
        extract_file();
    
        return 0;  
    }
    

    输出:

    parsing ALS 46000 yields:
    number only: 46000
    string ALS followed by nubmer 46000
    parsing BZK 39850 yields:
    number only: 39850
    string BZK followed by nubmer 39850
    parsing CAR 38000 yields:
    number only: 38000
    string CAR followed by nubmer 38000
    46000, 39850, 38000,
    

    【讨论】:

    • 虽然精神一开始看起来很复杂,但实际上使用起来非常简单:)
    【解决方案3】:

    您可以调用ignore 跳过指定数量的字符。

    istr.ignore(4);
    

    您也可以告诉它在分隔符处停止。您仍然需要知道前导字符串的最大字符数,但这也适用于较短的前导字符串:

    istr.ignore(10, ' ');
    

    你也可以编写一个循环,只读取字符直到你看到第一个数字字符:

    char c;
    while (istr.getchar(c) && !isdigit(c))
    {
        // do nothing
    }
    if (istr && isdigit(c))
        istr.putback(c);
    

    【讨论】:

    • 如果我从流中读取这些内容,我不确定如何实现。例如,while ( scoreFile >> foo ) 只会在 ALS 中读取。然后下次通过循环时,它将仅读取 int 。在这种情况下怎么能忽略帮助。
    • 你交替调用它们。 while (istr) { istr.ignore(4); int num = 0; istr &gt;&gt; num; /* do something with num */ }
    【解决方案4】:

    这里是:P

    private static void readFile(String fileName) {
    
            try {
                HashMap<String, Integer> map = new HashMap<String, Integer>();
                File file = new File(fileName);
    
                Scanner scanner = new Scanner(file).useDelimiter(";");
                while (scanner.hasNext()) {
                    String token = scanner.next();
                    String[] split = token.split(":");
                    if (split.length == 2) {
                        Integer count = map.get(split[0]);
                        map.put(split[0], count == null ? 1 : count + 1);
                        System.out.println(split[0] + ":" + split[1]);
                    } else {
                        split = token.split("=");
                        if (split.length == 2) {
                            Integer count = map.get(split[0]);
                            map.put(split[0], count == null ? 1 : count + 1);
                            System.out.println(split[0] + ":" + split[1]);
                        }
                    }
                }
                scanner.close();
                System.out.println("Counts:" + map);
            } catch (FileNotFoundException e) {
                e.printStackTrace();
            }
        }
    
        public static void main(String[] args) {
            readFile("test.txt");
        }
    }
    

    【讨论】:

    • 这似乎是 Java。 OP 要求使用 C++。
    • 这让我更喜欢 C++ 的简单性 :-)
    【解决方案5】:
    fscanf(file, "%*s %d", &num);
    

    或者 %05d 如果你有前导零并且固定宽度为 5....

    有时用 C++ 做事的最快方法是使用 C。:)

    【讨论】:

    • 我当然忘记了你使用的是 ifstream ......但你必须这样做吗?
    • 更喜欢更简单的C++方式:文件>>单词>>数字;
    【解决方案6】:

    您可以创建一个将字母分类为空白的 ctype facet。创建一个使用此构面的语言环境,然后用该语言环境填充流。这样,您可以从流中提取数字,但所有字母都将被视为空格(即,当您提取数字时,字母将被忽略,就像空格或制表符一样):

    这样的语言环境可能如下所示:

    #include <iostream>
    #include <locale>
    #include <vector>
    #include <algorithm>
    
    struct digits_only: std::ctype<char> 
    {
        digits_only(): std::ctype<char>(get_table()) {}
    
        static std::ctype_base::mask const* get_table()
        {
            static std::vector<std::ctype_base::mask> 
                rc(std::ctype<char>::table_size,std::ctype_base::space);
    
            if (rc['0'] == std::ctype_base::space)
                std::fill_n(&rc['0'], 9, std::ctype_base::mask());
            return &rc[0];
        }
    };
    

    使用它的示例代码如下所示:

    int main() {
        std::cin.imbue(std::locale(std::locale(), new digits_only()));
    
        std::copy(std::istream_iterator<int>(std::cin), 
            std::istream_iterator<int>(),
            std::ostream_iterator<int>(std::cout, "\n"));
    }
    

    使用您的示例数据,我得到的输出如下所示:

    46000
    39850
    38000
    

    请注意,就目前而言,我写这个是为了接受 only 数字。如果(例如)您正在读取浮点数,您还想保留 '.' (或特定于语言环境的等效项)作为小数点。处理事情的一种方法是从普通 ctype 表的副本开始,然后将要忽略的内容设置为 space

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2015-06-09
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2011-04-12
      • 2021-02-11
      • 2016-01-12
      • 1970-01-01
      相关资源
      最近更新 更多