【问题标题】:Python: splitting a text file by a # character and summarizing the totalPython:用#字符分割文本文件并总结总数
【发布时间】:2019-11-24 05:36:07
【问题描述】:

main()invoice.close() 和它上面的 print 函数都抛出“无效语法”异常。

我对字典或函数一无所知(目前),所以这是我能想到的最好的。

这是我想要的结果:

这里是invoice.txt的内容:

#### CONTENTS OF invoice.txt ###
# hammer#9.95
# saw#20.15
# shovel#35.40

编辑*** 这是添加括号的例外 enter image description here

print('{0: <10}'.format('Item'), '{0: >17}'.format('Cost'), sep = '' )

def main():
    invoice = open("invoice.txt", "r")

    count = 0
    total = 0

    hammer = invoice.readline()

    while invoice != "":
        saw = invoice.readline()
        shovel = invoice.readline()

        hammer = hammer.rstrip("\n")
        saw = saw.rstrip("\n")
        shovel = shovel.rstrip("\n")

        hammer = hammer.split("#")
        saw = saw.split("#")
        shovel = shovel.split("#")

        print('{0: <10}'.format(hammer[0]), '{0: >17}'.format('$' + hammer[1]), sep = '' )
        print('{0: <10}'.format(saw[0]), '{0: >17}'.format('$' + saw[1]), sep = '' )
        print('{0: <10}'.format(shovel[0]), '{0: >17}'.format('$' + shovel[1]), sep = '' )

        # total = total + float(hammer[1]) + float(saw[1]) + float(shovel[1])         # DOESN"T WORK
        total = total + (int(float(hammer[1])) + int(float(saw[1])) + int(float(shovel[1]))

        # total = total + (int(hammer[1])) + (int(saw[1])) + (int(shovel[1]))         # DOESN"T WORK
        print("{0: <10}".format("Total cost") + "{0: >17}".format("{0:.2f}".format(float(total))))

    invoice.close()
main()

【问题讨论】:

  • 欢迎来到 SO!请参阅this。您的文本文件是否真的以# 开头每一行并且第一行字面意思是#### CONTENTS OF invoice.txt ###?如果没有,请编辑这些。谢谢。
  • 总是将完整的错误消息(从单词“Traceback”开始)作为文本(不是屏幕截图)放在有问题的(不是评论)中。还有其他有用的信息。
  • 您似乎在以下行的末尾缺少一个右括号:total = total + (int(float(hammer[1])) + int(float(saw[1])) + int(float(shovel[1])). 应该是 total = total + (int(float(hammer[1])) + int(float(saw[1])) + int(float(shovel[1] )))

标签: python split hammer


【解决方案1】:

假设内容实际上只是

hammer#9.95
saw#20.15
shovel#35.40

这只是一个使用# 作为分隔符的DSV。幸运的是,大多数CSV 程序都支持多个分隔符,包括 python 的。

import csv
from io import StringIO 


def print_report(items, fields):
    print("\t".join(fields))
    total_cost = sum(float(item["Cost"]) for item in items) 
    for item in items:
        print(f"{item['Item']}\t${float(item['Cost']):.2f}")
    print()
    print(f"Total cost\t${total_cost:.2f}")
    print(f"Number of tools\t{len(items)}")

fields = ["Item", "Cost"]
# This StringIO is basically like opening a file
s = StringIO(u"hammer#9.95\nsaw#20.15\nshovel#35.40")
items = list(csv.DictReader(s, delimiter="#", fieldnames=fields))
print_report(items, fields)

Item    Cost
hammer  $9.95
saw $20.15
shovel  $35.40

Total cost  $65.50
Number of tools 3

【讨论】:

    【解决方案2】:

    这里的许多格式化代码都是多余的,并且对文本文件中的名称进行硬编码是不可扩展的。如果您需要引入新项目,则必须更改所有代码。我们应该通过编写一个辅助函数来处理打印来保持DRYformat 可以接受多个参数以简化问题。

    其他一切都是读取文件的问题(使用上下文管理器with,这样您就不必担心记得调用close)、匹配有效行、拆分、剥离和转换数据。一旦所有内容都在列表中,我们就可以调用该列表中的 sumlen 函数来生成所需的总数。

    我已经硬编码了列大小——这些确实应该是参数,我们应该循环数据以预先计算列名。但这可能是矫枉过正。

    如果您的数据实际上是 CSV,但其余代码几乎相同,请使用 CSV 库。

    import re
    
    def fmt(x, y):
        if isinstance(y, float):
            y = "$" + "{:1,.2f}".format(y).rjust(5)
    
        return '{0:<18}{1:>10}'.format(x, y)
    
    if __name__ == "__main__":
        items = []
    
        with open("invoice.txt", "r") as f:
            for line in f:
                if re.search(r"#\d+\.\d+$", line):
                    items.append([x.strip() for x in line.split("#") if x])
    
        print(fmt("Name", "Cost"))
    
        for name, price in items:
            print(fmt(name, float(price)))
    
        print()
        print(fmt("Total cost", sum([float(x[1]) for x in items])))
        print(fmt("Number of tools", len(items)))
    

    输出:

    Name                    Cost
    hammer                $ 9.95
    saw                   $20.15
    shovel                $35.40
    Total cost            $65.50
    Number of tools            3
    

    【讨论】:

      【解决方案3】:

      语法错误

      让我们退后一步,看看您的代码为什么不起作用。 这是我在运行您的代码时收到的错误消息:

        File "test_2.py", line 31
          print("{0: <10}".format("Total cost") + "{0: >17}".format("{0:.2f}".format(float(total))))
              ^
      SyntaxError: invalid syntax
      

      在阅读 Python 中的错误消息时,有很多信息一开始可能会让人不知所措,但一旦你学会了如何提取这些信息,它们真的很有帮助!让我们分解错误消息:

      • 错误在test_2.py,第 31 行。test_2 是我在这种情况下称为我的文件,你的可能不同。
      • 错误类型为语法错误,表示该行代码无法被 Python 正确解析。
      • 注意print statement 下方的箭头:它准确地指示在解析过程中出现错误的符号。

      弹出的一个非常常见的语法错误是括号不匹配。使这个错误更加棘手的是,解析器会告诉你它发现括号匹配问题的地方,但问题可能位于完全不同的行上。这正是这里发生的情况,所以我对 syndax 错误的一般规则是检查错误告诉您检查的行,以及之前和之后的几行。 让我们仔细看看您指出不起作用的两行:

      total = total + (int(float(hammer[1])) + int(float(saw[1])) + int(float(shovel[1]))
      
      print("{0:<10}".format("Total cost") + "{0:>17}".format("{:.2f}".format(float(total))))
      

      在第一行中,您打开一个括号(,然后分别在hammersawshovel 上调用int()float(),但您永远不会放下一个右括号!所以错误似乎比 Python 说的要早,但从解析器的角度来看,目前实际上没有问题。直到解析器到达第 31 行,我们才遇到问题;解析器期待一个右括号,但它得到了一个 print 函数,并在第 31 行的 print 函数上引发了错误,而不是在缺少括号的前一行。

      这些类型的事情需要时间来适应,但随着时间的推移,你会学到技巧和窍门。

      更好地使用变量

      在您的 Python 程序中,您不应假定文本文件的内容。目前,您假设只有 3 个项目,并且您的变量名称似乎反映了您假设这些项目将是什么。如果文本文件包含数千个项目会怎样?为文本文件中的每个项目创建一个新变量是不可行的。您应该使用通用名称,例如 tool,而不是 hammersawshovel,并在循环的单独迭代中处理文本文件中的每个项目,而不是一次处理所有项目。

      示例解决方案

      这里有一个示例解决方案。就像你说的那样,使用字典会很好,所以下面的解决方案实现了一个返回项目字典的函数,其中键是项目名称,值是项目成本。然后使用get_invoice 函数返回发票的格式化字符串,并打印出发票。

      def main():
          try:
              products = read_products("invoice.txt")
          except IOError as e:
              print('An error occurred while opening the file:', e)
              return
          except ValueError as e:
              print('An error occurred while reading the contents of the file:', e)
              return
          invoice = get_invoice(products)
          print(invoice)
      
      def get_invoice(products):
          footer_headings = ('Total cost', 'Number of tools')
      
          # Get the length of the longest product name, to aid formatting.
          l_pad = len(max(products, key=lambda k: len(k)))
          l_pad = max(l_pad, len(max(footer_headings, key=lambda h: len(h))))
          r_pad = 10
      
          total = sum(products.values())
          count = len(products)
      
          header = 'Item'.ljust(l_pad) + 'Cost'.rjust(r_pad)
          body = '\n'.join([f'{p[0]}'.ljust(l_pad) + (f'${p[1]:.2f}').rjust(r_pad) for p in products.items()])
          footer = f'{footer_headings[0]}'.ljust(l_pad) + f'${total:.2f}'.rjust(r_pad) + '\n' + f'{footer_headings[1]}'.ljust(l_pad) + f'{count}'.rjust(r_pad)
      
          return f'{header}\n{body}\n\n{footer}'
      
      def read_products(file_path):
          products = {}
      
          with open(file_path, "r") as f:
              for line in f:
                  # Split the data
                  data_split = line.rstrip("\n").split("#")
      
                  # Ensure the data is safe to unpack
                  if len(data_split) != 2:
                      raise ValueError(f'Expected two fields but got {len(data_split)}')
      
                  # Add the data to a dictionary for later use
                  name, cost = data_split
                  cost = float(cost)
                  products[name] = cost
      
          return products
      
      if __name__ == "__main__":
          main()
      

      【讨论】:

      • 我建议使用上下文管理器来处理文件。 F弦也很高兴看到。
      • 现在更新解决方案:)
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2023-01-10
      • 1970-01-01
      • 2018-09-03
      • 2014-08-15
      • 1970-01-01
      相关资源
      最近更新 更多