【问题标题】:Python - How to split a string by non alpha charactersPython - 如何用非字母字符分割字符串
【发布时间】:2016-05-15 20:26:01
【问题描述】:

我正在尝试使用 python 来解析 c++ 源代码行。我唯一感兴趣的是包含指令。

    #include "header.hpp"

我希望它灵活,并且仍然适用于糟糕的编码风格,例如:

          #   include"header.hpp"  

我已经到了可以在 # 前后读取行和修剪空格的地步。但是,我仍然需要通过读取字符串来找出它是什么指令,直到遇到非字母字符,无论天气如何,它是空格、引号、制表符或尖括号。

所以基本上我的问题是:如何拆分以 alpha 开头的字符串,直到遇到非 alpha?

我认为我可以使用正则表达式来做到这一点,但我在文档中没有找到任何我想要的东西。

另外,如果有人对我如何在引号或尖括号内获取文件名有任何建议,那将是一个加号。

【问题讨论】:

  • 您要拆分还是检索“header.hpp”部分?你能给出示例输出吗?
  • 请包含示例字符串和预期结果。
  • 如果指令是其他的,那么包含它并不重要,我将跳到下一行,但如果它是一个包含指令,我将需要“he​​ader.hpp”部分。跨度>
  • 示例字符串:"#include "header.hpp"" 如果指令包含所需的输出:"header.hpp"(或""header.hpp"")
  • 纯python,还是允许库建议?

标签: python regex string parsing


【解决方案1】:

您使用正则表达式的直觉是正确的。

import re
re.split('[^a-zA-Z]', string_to_split)

[^a-zA-Z] 部分的意思是“不是字母字符”。

【讨论】:

  • 这个答案很危险,因为它没有被空格分割。
  • 它为我分割空间。似乎它会分裂除字母字符之外的任何内容。它只排除 a-z 和 A-Z - 所以它是一个非常有限的集合。
  • 为什么不简单地使用re.split('\W', string_to_split)(或者,如果您还想排除空格,re.split('\W+', string_to_split))?
  • \W 将拆分数字和下划线以及非字母字符。 docs.python.org/3/library/re.html
【解决方案2】:

您可以使用正则表达式来做到这一点。不过,您也可以使用简单的while 循环。

def splitnonalpha(s):
   pos = 1
   while pos < len(s) and s[pos].isalpha():
      pos+=1
   return (s[:pos], s[pos:])

测试:

>>> splitnonalpha('#include"blah.hpp"')
('#include', '"blah.hpp"')

【讨论】:

  • 我选择这个答案是因为 a) 这意味着我不必处理正则表达式,b) 其他解决方案在其他预处理器指令(如定义)下失败,而这个不是
  • @nickeb96 - 我建议你找一个真正的 C 解析器。这(是最糟糕的)与所有其他答案一样无法接近。
  • @sln 我稍后可能会看看 C 解析器,但由于我现在真的只需要查看包含指令,因此我正在寻找一个轻量级的解决方案。我基本上只是在制作一个简单的 makefile 生成器,比如 bakefile 或无数其他的
  • @nickeb96 - 必须手动检查准确性的生成器根本没有用。
  • @sln 解析所有源文件的重点是不必手动检查。
【解决方案3】:

其他人提到的我认为最好的两个选项是re.splitre.findall

>>> import re
>>> re.split(r'\W+', '#include "header.hpp"')
['', 'include', 'header', 'hpp', '']
>>> re.findall(r'\w+', '#include "header.hpp"')
['include', 'header', 'hpp']

快速基准测试:

>>> setup = "import re; word_pattern = re.compile(r'\w+'); sep_pattern = re.compile(r'\W+')"
>>> iterations = 10**6
>>> timeit.timeit("re.findall(r'\w+', '#header foo bar!')", setup=setup, number=iterations)
3.000092029571533
>>> timeit.timeit("word_pattern.findall('#header foo bar!')", setup=setup, number=iterations)
1.5247418880462646
>>> timeit.timeit("re.split(r'\W+', '#header foo bar!')", setup=setup, number=iterations)
3.786440134048462
>>> timeit.timeit("sep_pattern.split('#header foo bar!')", setup=setup, number=iterations)
2.256173849105835

功能上的区别在于re.split 保留空标记。这对于标记化目的通常没有用,但以下应该与 re.findall 解决方案相同:

>>> filter(bool, re.split(r'\W+', '#include "header.hpp"'))
['include', 'header', 'hpp']

【讨论】:

    【解决方案4】:

    您可以使用正则表达式。 \W 标记将匹配所有非单词字符(与非字母数字字符大致相同)。单词字符为A-Za-z0-9_。如果你也想匹配下划线,你可以做[\W_]

    >>> import re
    >>> line = '#   include"header.hpp"  ' 
    >>> m = re.match(r'^\s*#\s*include\W+([\w\.]+)\W*$', line)
    >>> m.group(1)
    'header.hpp'
    

    【讨论】:

    • 唯一的问题是如果我用类似定义的东西替换包含我得到一个 AttributeError: 'NoneType' object has no attribute 'group' 所以我需要一种方法来判断它是否是一个包含执行此代码之前的指令
    • 大多数 C 解析器不允许在 #include 之间使用换行符,而 *#\s*include 将允许它。
    【解决方案5】:
    import re
    s = 'foo bar- blah/hm.lala'
    print(re.findall(r"\w+",s))
    

    输出:['foo', 'bar', 'blah', 'hm', 'lala']

    【讨论】:

      【解决方案6】:
      import re
      re.split('[^a-zA-Z0-9]', string_to_split)
      

      对于所有 !(字母数字)字符

      【讨论】:

      • 嘿,你能解释一下它的作用吗?
      【解决方案7】:

      虽然不准确,但大多数解析头指令都是这样的

      (?m)^\h*#\h*include\h*["&lt;](\w[\w.]*)\h*["&gt;]

      其中,(?m) 是多行模式,\h 是水平空格(又名 [^\S\r\n] )。

      【讨论】:

        【解决方案8】:

        这行得通:

        import re
        
        test_str = '    #   include "header.hpp"'
        
        match = re.match(r'\s*#\s*include\s*("[\w.]*")', test_str)
        if match:
            print match.group(1)
        

        【讨论】:

        • 唯一的问题是如果我用类似定义的东西替换包含我得到一个 AttributeError: 'NoneType' object has no attribute 'group'
        • 替换为正则表达式还是输入字符串?为什么不先看看是否找到匹配项...
        • 啊它现在可以与 if 语句一起使用。如果只有堆栈溢出会让我接受 2 个答案。
        猜你喜欢
        • 2020-06-30
        • 1970-01-01
        • 2021-01-22
        • 2018-12-20
        • 2018-09-08
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多