【问题标题】:Comparing elements of text file比较文本文件的元素
【发布时间】:2020-01-20 10:52:26
【问题描述】:

我正在尝试将四个数字的块相互比较,以创建一个新的输出文件,其中仅包含满足该条件的输出文件:所有数字都相同的四位数字。

这是我的输入文件代码:

int main() { ofstream outfile ("text.txt"); outfile << "1111 1212 4444 \n 2222 \n \n 8888 4567" <<endl;

我想将其拆分为四个块,例如“1111”、“1212”等,以便仅在新输出文件中写入满足要求的块。我决定将整个文件转换为一个整数向量以便能够比较它们。

   char digit;
   ifstream file("text.txt");
   vector <int> digits;

   while(file>>digit)
   {
      digits.push_back(digit - '0');
   }

我想比较它们的方法看起来像这样:

bool IsValid(vector<int> digits){

   for (int i=0; i<digits.size() i++)
   {
      if(digits[0] == digits[1] == digits[2] == digits [3])
         return true; 

      else 
      {
         return false;
      }
   }
}

但是这只会比较第一个块,你会做不同的吗?还是我应该继续做向量的想法。

【问题讨论】:

  • 而不是读取单个字符,为什么不将数字读取为字符串?你只需要读取一个数字,如果它满足条件将它写入另一个文件,继续下一个数字......
  • @Aconcagua 我的第一个问题是读取不起作用的整数,但是使用字符串我看不到问题。使用std::string 可以轻松实现位数和所有相同的位数
  • @foreknownas_463035818 啊,好像是我疏忽了。当然,用字符串,那很好......
  • 请不要写if(condition) return true; else return false;之类的代码,那是不好的风格。只需写return condition;
  • 请注意digits[0] == digits[1] == digits[2] == digits [3] 不会按照您的想法行事。首先将比较 digits[0] == digits[1] 并返回一个布尔值,例如真的。接下来,digits[2] 与该值进行比较。 2211 的结果将为真:wandbox.org/permlink/rvlz2Mn7YTKEUmOS

标签: c++ file c++11 text


【解决方案1】:

嗯,我所看到的都是相当复杂的。

显然您想检查字符串中的模式。并且模式通常与正则表达式匹配。

这将为您提供一个非常简短的解决方案。使用std::regex。正则表达式是 C++ 标准库的一部分。而且它们也很容易使用。对于您的情况,您的正则表达式是(\d)\1{3}。所以,一个数字后跟 3 个相同的数字。

程序然后归结为一个语句:

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

std::istringstream testData{R"(1111 1212 444414 555
2222

8888 4567)"};

int main()
{
    std::copy_if(
        std::istream_iterator<std::string>(testData), 
        {},
        std::ostream_iterator<std::string>(std::cout,"\n"),
        [](const std::string& s){
            return std::regex_match(s,std::regex(R"((\d)\1{3})"));
        }
    );

    return 0;
}

当然,您可以使用任何std::fstream 代替std::istringstream

当然,这只是众多可能的解决方案之一,而且可能不是最好的解决方案。 . .

【讨论】:

  • 很好,得到了我的投票——但是copy_if 中发生了很多事情,所以你应该解释一下细节。我仍然认为提取int 并检查它是否是1111 的倍数既更快又更直观,但这很好。
【解决方案2】:

我决定将整个文件转换成一个整数向量,以便能够比较它们。

然后您可以直接从流中提取ints (file &gt;&gt; int_variable) 并检查它们是否是 1111 的倍数。

代码中的建议:

#include <fstream>
#include <iomanip>
#include <iostream>
#include <vector>

bool IsValid(int number) {
    // Check that number is in the valid range and that it's a multiple of 1111.
    return number >= 0 && number <= 9999 && (number / 1111) * 1111 == number;
}

// A function to process the values in a stream
std::vector<int> process_stream(std::istream& is) {
    std::vector<int> digits;
    int number;

    while(is >> number) {
        if(IsValid(number)) // Only save valid numbers
            digits.push_back(number);
    }
    return digits;
}

int main() {
    std::vector<int> digits;

    // Check that opening the file succeeds before using it
    if(std::ifstream file = std::ifstream("text.txt")) {
        digits = process_stream(file);
    }

    // Print the collected int:s
    for(int x : digits) {
        std::cout << std::setw(4) << std::setfill('0') << x << '\n';
    }
}

【讨论】:

  • 如果允许带前导零的数字,该测试将失败 (0077)...
【解决方案3】:

另一种方法是将每个输入简单地处理为一个字符串,然后循环遍历字符串中的每个字符,以验证它是一个数字并且等于前一个字符。如果它没有通过任何一个测试,那么读取的不是所有数字都相等的整数。

例如你可以这样做:

#include <iostream>
#include <sstream>
#include <string>
#include <cctype>

int main (void) {

int main (void) {

    std::string s;
    std::stringstream ss { "1 11 1111 foo 2222\nbar 1212\n4444\n8888\n4567\n"
                            "3433333 a8\n9999999999999999999\n" };

    while (ss >> s) {                               /* read each string */
        bool equaldigits = true;                    /* flags equal digits */
        for (size_t i = 1; i < s.length(); i++)     /* loop 1 - length */
            /* validate previous & current digits & equal */
            if (!isdigit(s[i-1]) || !isdigit(s[i]) || s[i-1] != s[i]) {
                equaldigits = false;                /* if not set flag false */
                break;                              /* break loop */
            }
        /* handle empty-string or single char case */
        if (!s.length() || (s.length() == 1 && !isdigit(s[0])))
            equaldigits = false;
        if (equaldigits)                            /* if all digits & equal */
            std::cout << s << '\n';                 /* output string */
    }
}

上面的std::stringstream 只是为程序提供了模拟输入。

(注意:如果您愿意,可以使用std::string::iterator 循环,或者使用基于范围的for 循环和prev char 来存储最后看到的。在这里,就像易于迭代索引)

使用 std::string find_first_not_of

使用现有的字符串函数提供了另一种方法。在比较第一个字符是数字之后,您可以使用std::basic_string::find_first_not_of 扫描字符串的其余部分以查找与第一个字符不同的字符 - 如果结果不是std::string::npos,那么您的字符串不都是同一个数字。

#include <iostream>
#include <sstream>
#include <string>
#include <cctype>

int main (void) {

    std::string s;
    std::stringstream ss { "1 11 1111 foo 2222\nbar 1212\n4444\n8888\n4567\n"
                            "3433333 a8\n9999999999999999999\n" };

    while (ss >> s) {                               /* read each string */
        if (!isdigit(s.at(0)))                      /* 1st char digit? */
            continue;
        /* if remainder of chars not equal 1st char - not equal digits */
        if (s.find_first_not_of(s.at(0)) != std::string::npos)
            continue;
        std::cout << s << '\n';
    }
}

两种方法产生相同的输出。

使用/输出示例

$ ./bin/intdigitssame
1
11
1111
2222
4444
8888
9999999999999999999

如其他好的答案所示,还有许多其他方法可以做到这一点。每种方法都值得了解。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2022-08-16
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多