【问题标题】:Converting a text file with math problems to a text file with the answers to those problems将包含数学问题的文本文件转换为包含这些问题答案的文本文件
【发布时间】:2018-05-16 17:16:59
【问题描述】:

我目前正在尝试编写一个代码,该代码接受用户的输入,要求他们打开一个包含数学问题的文件,然后输出一个包含这些问题答案的文件。我已经到处寻找某种解决方案,但这些是我得到的最接近的:

文本文件内容:

2 + 2

3 * 3

4.5 - 2.3

-8.8 + 1.2

2 ^ 4

8.9 / 2.3

5 % 3

-2 * -2

尝试 1:

input_file_name = input("What file would you like to open? ")
output_file_name = input("What file would you like to write to? ")

with open(input_file_name,"r") as input_file:
    contents = input_file.readlines()
    num = "".join(contents)
    contents_length = len(num)

with open(output_file_name, "w") as output_file:
    while contents_length >= 0:
        num = num.replace("^","**") # change the ^ to ** so python can properly do the function
        contents_value = exec(num)

        contents_length = contents_length - 1

    output_file.write(str(contents_value))

我收到的文本文件会返回“无”

尝试 2:

input_file_name = input("What file would you like to open? ")
output_file_name = input("What file would you like to write to? ")

infile = open(input_file_name, "r")
outfile = open(output_file_name, "w")

lines = infile.readlines()

i = len(lines) - 1

while i >= 0:
    ans = eval(lines[i])
    outfile.write(str(ans))
    i = i - 1

infile.close()
outfile.close()

并且文本文件中有 423.86956521739130476-7.60000000000000052.294 在文件中。

所以,我真的不确定还能做什么。任何帮助将不胜感激。

【问题讨论】:

    标签: python string file math text


    【解决方案1】:

    你需要单独处理每一行,但是这段代码

    num = "".join(contents)
    

    将所有行组合成一个字符串。

    另一个问题是不需要的小数位。您可以使用 round 函数解决此问题。下面的代码直接从脚本中读取数据,但是很容易让它适应从文件中读取数据,或者使用.readlines,或者通过简单地循环文件对象。

    data = '''\
    2 + 2
    3 * 3
    4.5 - 2.3
    -8.8 + 1.2
    2 ^ 4
    8.9 / 2.3
    5 % 3
    -2 * -2
    '''.splitlines()
    
    for line in data:
        line = line.replace('^', '**')
        s = round(eval(line), 6)
        print(s)
    

    输出

    4
    9
    2.2
    -7.6
    16
    3.869565
    2
    4
    

    该输出是使用 Python 3 创建的。在 Python 2 上,它不是很干净:所有内容都将被写为浮点数。

    Python 2 输出

    4.0
    9.0
    2.2
    -7.6
    16.0
    3.869565
    2.0
    4.0
    

    我假设您知道 evalexec 通常应该避免使用,因为它们可能存在安全风险。有关详细信息,请参阅 SO 老将 Ned Batchelder 的Eval really is dangerous。但是我想你在这里没有太多选择,你可以保证输入文件不会包含恶意代码。

    【讨论】:

    • 啊,明白了!非常感谢!
    • 我不知道,我今天居然学了eval和exec,哈哈。但是感谢您的提醒!
    • 如果我希望输出文件具有原始表达式 + = + 答案怎么办?
    • @bcochran 一种方法是使用格式组合两个字符串:print('{} = {}'.format(line, s))。另一种方法是使用printsep 参数,例如:print(line, s, sep=' = ')。顺便说一句,print 采用 file 参数,因此您可以使用它直接打印到打开的文本文件。
    【解决方案2】:

    您在第二次尝试中几乎得到了正确答案。每次将内容写入文件时只需添加一个换行符,我认为它会正确格式化

    outfile.write(str(ans) + '\n')
    

    PS我没有测试过,请检查是否有语法错误

    【讨论】:

    • 我试过这个,它确实把所有的“答案”都放在了自己的行上,但只有第一个问题的答案是正确的。不过谢谢你,又近了一步!编辑:实际上,第一个、第四个和最后一个答案是正确的。
    • 编辑:答案是对的,只是顺序不对,哈哈。
    • 我不明白,为什么答案的顺序不正确?
    • 我认为这是因为我让程序检查线路的方式?我的第二次尝试来自我们在课堂上做的一个例子,我的教授让我们编写一个程序,该程序基本上获取一个文件并翻转其中的内容。
    【解决方案3】:

    是的,你想使用 eval() 而不是 exec() 是对的。

    看起来你只需要在每个解决方案之后写一个换行符,即

    outfile.write('\n')
    

    【讨论】:

    • 我试过这个,虽然每个答案都在自己的行上,但唯一正确的答案是第一个。不过,谢谢!
    • 编辑:答案是对的,只是顺序不对,哈哈。
    【解决方案4】:

    为了论证,我想证明您根本不必使用eval 来解决您的问题!作为PM 2Ring has warned,使用evalexec 是一种安全隐患,以防您正在评估的字符串有可能被恶意第三方篡改。许多人忽略了这种安全风险,但如果您阅读my favourite answer in the subject,您会看到一个非常简单的示例,即通过调用eval 来执行python 之外的任意程序。因此,应该永远不要在不是 100% 受信任和控制的来源上使用 eval/exec

    回到你的问题,你的例子看起来模式总是val1 OP val2,即带有中缀数学运算符的两个数字(可能是负数,可能是浮点数)。这种结构非常简单,您可以(而且我相信应该)为它编写自己的解析器。这证明了通过以一种安全的方式明确处理我们期望的输入,我们可以保证我们的程序在无效和恶意输入上中断。

    这是一个返回输入的一行(一个表达式)结果的函数:

    import operator
    
    def evaluate(expr):
        '''Evaluate expressions of the form `val1 OP val2`; OP is in [+,-,/,*,%,^]'''
        ops = {
            '+': operator.add,
            '-': operator.sub,
            '*': operator.mul,
            '/': operator.truediv,
            '%': operator.mod,
            '^': operator.pow,
            }
        # gather operators and put minus last so we can handle negative numbers right
        opskeys = list(ops)
        ind = opskeys.index('-')
        implemented_ops = opskeys[ind+1:] + opskeys[:ind+1]
    
        for opnow in implemented_ops:
            if opnow in expr:
                val1,op,val2 = expr.rpartition(opnow)
                # convert the numbers to floats, or ints in case they are integral
                val1,val2 = (int(val) if val.is_integer() else val
                             for val in map(float,[val1,val2]))
                break
        else:
            raise ValueError('Invalid input line "{}"!'.format(expr))
    
        return ops[op](val1,val2) # or round(...,digits) see PM 2Ring's answer
    

    该函数接受单行输入,找到适当的数学运算符,使用该字符分割行,然后计算结果。它使用 dispatch dict 根据字符选择实际的数学运算符;一个好处是我们甚至不必转换幂运算符。我们将整数输入转换为整数,其余的保留为浮点数。

    该函数最后处理减法,这确保负数不会与其他所有运算符混淆计算。这并不完全是万无一失的:如果有一个无效的行包含意想不到的负数,我们可能会从val1/val2 的第一次转换中得到一个ValueErrorfloat。从负数或类似的极端情况中减去数字时,它也可能会中断。可以在代码中添加进一步的保护措施(例如捕获来自float() 的异常);我的意思是证明您可以非常轻松地使用安全可控的方法,而不是 eval

    使用您的数据运行的示例(再次向 PM 2Ring 提示)加上故意无效的输入行:

    data = '''\
    2 + 2
    3 * 3
    4.5 - 2.3
    -8.8 + 1.2
    2 ^ 4
    8.9 / 2.3
    5 % 3
    -2 * -2
    3 @ 4
    '''.splitlines()
    
    for line in data:
        print(evaluate(line))
    

    交互式shell中的相应输出:

    4
    9
    2.2
    -7.6000000000000005
    16
    3.8695652173913047
    2
    4
    Traceback (most recent call last):
      File "<stdin>", line 2, in <module>
      File "foo.py", line 26, in evaluate
        raise ValueError('Invalid input line "{}"!'.format(expr))
    ValueError: Invalid input line "3 @ 4"!
    

    【讨论】:

      猜你喜欢
      • 2011-06-17
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2014-11-29
      • 2023-03-04
      • 1970-01-01
      • 2013-03-08
      • 2014-05-30
      相关资源
      最近更新 更多