【问题标题】:Return value in a quite nested for-loop在非常嵌套的 for 循环中返回值
【发布时间】:2019-02-17 15:19:46
【问题描述】:

我想要嵌套循环来测试是否所有元素都符合条件,然后返回 True。示例:

有一个给定的文本文件:file.txt,其中包含这种模式的行:

aaa:bb3:3

fff:cc3:4

字母、冒号、字母数字、冒号、整数、换行符。

一般来说,我想测试是否所有的行都匹配这个模式。但是,在这个函数中,我想检查第一列是否只包含字母。

def opener(file):
    #Opens a file and creates a list of lines
    fi=open(file).read().splitlines()
    import string
    res = True
    for i in fi:
        #Checks whether any characters in the first column is not a letter
        if any(j not in string.ascii_letters for j in i.split(':')[0]):
             res = False
        else:
            continue
    return res

但是,即使第一列中的所有字符都是字母,该函数也会返回 False。我也想请你解释一下。

【问题讨论】:

    标签: python python-3.x for-loop


    【解决方案1】:

    您的代码评估代码后的空行 - 因此 False

    您的文件在其最后一行之后包含一个换行符,因此您的代码会检查最后一个数据之后的行,这未完成您的测试 - 这就是为什么无论输入如何都会得到 False

    aaa:bb3:3
    fff:cc3:4
                        empty line that does not start with only letters
    

    如果空行出现在末尾,您可以“特别处理”空行来修复它。如果您在填充的行之间有一个空行,您也可以返回 False

    with open("t.txt","w") as f:
        f.write("""aaa:bb3:3
    fff:cc3:4
    """) 
    
    import string 
    def opener(file):
        letters = string.ascii_letters
        # Opens a file and creates a list of lines
        with open(file) as fi:
            res = True
            empty_line_found = False
            for i in fi:
                if i.strip(): # only check line if not empty
                    if empty_line_found:  # we had an empty line and now a filled line: error
                        return False
                #Checks whether any characters in the first column is not a letter
                    if any(j not in letters for j in i.strip().split(':')[0]):
                        return False   # immediately exit - no need to test the rest of the file
                else:
                    empty_line_found = True
    
        return res # or True
    
    
    print (opener("t.txt"))
    

    输出:

    True
    

    如果你使用

    # example with a file that contains an empty line between data lines - NOT ok
    with open("t.txt","w") as f:
        f.write("""aaa:bb3:3
    
    fff:cc3:4
    """) 
    

    # example for file that contains empty line after data - which is ok
    with open("t.txt","w") as f:
        f.write("""aaa:bb3:3
    ff2f:cc3:4
    
    
    """) 
    

    你得到:False

    【讨论】:

    • 你能解释一下empty_line_found值的逐渐变化吗?尽管您的帖子可能被归类为最全面的帖子之一,但变量empty_line_found 的生活史让我感到困惑。
    • @fgh 你在阅读之前初始化empty_line_found=False - 一旦你找到一个空行,你就将它设置为True .. 如果任何“非空”行在处理后你知道你的数据行中有一个空行,可以返回False。如果您之后只遇到空行,您的文件只是在数据后面有 1 到 n 个空行,您可以返回 res - 一旦遇到不匹配,代码就会返回 False - 所以你当错误在第 2 行时,不要解析 200 万行...
    • 谢谢,我明白你的意思。但是,我看到另一个问题-我不知道是文件问题还是我的理解。 splitlines() 不会从行尾删除换行符吗?我想我们考虑一个包含字符串'aa:b3:45\naaa:b4:56\n' 的文件并接收['aa:b3:45', 'aaa:b4:56']
    • @fgh - 是的 -。但如果其中有空格,它不会删除最后一个换行符 after 之后的空行:with open("f.txt","w") as f:f.write('aa:b3:45\naaa:b4:56\n ') && k = open("f.txt").readlines() # ['aa:b3:45\n', 'aaa:b4:56\n', ' '] 或者如果你有一个双 \n 在它的末尾
    【解决方案2】:

    结肠镜检查

    1. ASCII 和 UNICODE 都将字符 0x3A 定义为 COLON。这个字符看起来像两个点,一个接一个::

    2. ASCII 和 UNICODE 都将字符 0x3B 定义为 SEMICOLON。这个字符看起来像一个逗号上的点:;

    您在示例中对 分号 的使用始终如一:fff:cc3:4 并且您在描述性文本中对 分号 一词的使用始终如一:@ 987654324@

    我假设您的意思是 冒号 (':'),因为那是您输入的字符。如果不是,您应该在任何必要的地方将其更改为分号 (';')。

    您的代码

    这是你的代码,供参考:

    def opener(file):
        #Opens a file and creates a list of lines
        fi=open(file).read().splitlines()
        import string
        res = True
        for i in fi:
            #Checks whether any characters in the first column is not a letter
            if any(j not in string.ascii_letters for j in i.split(':')[0]):
                 res = False
            else:
                continue
        return res
    

    您的问题

    你问的问题是函数总是返回 false。您提供的示例在第一个示例和第二个示例之间包含一个空行。我会提醒您注意这些空白行中的空格或制表符。您可以通过显式捕获空白行并跳过它们来解决此问题:

    for i in fi:
        if i.isspace():
            # skip blank lines
            continue
    

    其他一些问题

    下面是您可能没有注意到的其他一些事情:

    1. 您在函数中提供了很好的评论。那应该是一个文档字符串:

      def opener(file):
          """ Opens a file and creates a list of lines.
          """
      
    2. import string 在你的函数中间。不要那样做。移动导入 直到模块顶部:

      import string # at top of file
      
      def opener(file):   # Not at top of file
      
    3. 您使用open() 打开了文件,但从未关闭过它。这正是为什么with关键字被添加到python:

      with open(file) as infile:
          fi = infile.read().splitlines()
      
    4. 您打开文件,将其全部内容读入内存,然后将其拆分为行 最后丢弃换行符。所有这些都是为了你可以用冒号分割它并忽略 除了第一个字段之外的所有内容。

      在文件上调用readlines() 会更简单:

      with open(file) as infile:
          fi = infile.readlines()
      
          res = True
      
          for i in fi:
      

      直接迭代文件会更容易并且更简单

      with open(file) as infile:
          res = True
          for i in infile:
      
    5. 您似乎正在努力检查您在开始时提供的整个格式。我怀疑正则表达式会(1)更容易编写和维护; (2) 以后更容易理解; (3) 执行速度更快。现在,对于这个简单的案例,以及稍后当您有更多规则时:

      import logging
      import re
      
      bad_lines = 0
      for line in infile:
          if line.isspace():
              continue
          if re.match(valid_line, line):
              continue
          logging.warn(f"Bad line: {line}")
          bad_lines += 1
      return bad_lines == 0
      
    6. 你的名字很糟糕。您的函数包括名称 filefiijres。唯一没有意义的是file.

      考虑到您要求人们阅读您的代码并帮助您发现问题,使用更好的名称。如果您只是将这些名称替换为 file(相同)、infilelinechresult,则代码的可读性会更高。如果您使用标准 Python 最佳实践(如 with)重构代码,它的可读性会更高。 (并且错误更少!)

    【讨论】:

    • 感谢您提供如此有用的提示 - 我只是需要您的全面建议。确实,我的目标是添加正则表达式;但是,我想先练习简单的部分解决方案并理解它们。
    猜你喜欢
    • 1970-01-01
    • 2011-12-10
    • 1970-01-01
    • 1970-01-01
    • 2021-06-25
    • 2015-01-28
    • 1970-01-01
    • 2012-12-27
    • 1970-01-01
    相关资源
    最近更新 更多