【问题标题】:How to write a small tokenizer in Python? [duplicate]如何在 Python 中编写一个小的分词器? [复制]
【发布时间】:2016-07-29 20:27:32
【问题描述】:

通常,Python 通过

调用函数
func(arg0, arg1)

但我想改成

func arg0 arg1

例如,

#Something...
cmd = input()
interpret(cmd)
#Something...

如果我输入'func arg0 arg1',那么我希望Python 执行func(arg0, arg1)

Args 会包含字符串,所以我们不能简单地拆分单词。

实际上,我想写一些脚本在我的手机上使用。所以打括号会有点烦人。

【问题讨论】:

  • 所以你想重写 Python 解析器来创建你自己的语言......?我认为这有点超出了范围。
  • @deceze 没有。因为我想在手机的终端上使用它。在我的手机上输入括号很烦人。
  • 所以您只想从输入中拆分第一个单词,按该名称查找函数,然后使用其余参数调用它,例如 f(*args)...?
  • 是的。但是 arg 可能是一个字符串,例如 ''Hello, World!'。所以我认为不应该简单地拆分它们。我想应该有一个小解析器。
  • 最终,您正在寻求创建一种新的迷你语言。如果你需要考虑带空格等的字符串,你需要一个解析器,并且 boom,你在 DSL 领域。

标签: python parsing interpreter


【解决方案1】:

你可以这样做:

class  tryClass:
    def callFunction(self, arg, arg2):
        print("In call")
        print(arg)
        print(arg2)

input = str(input())
input = input.split(" ")
funcName = input[0]
my_cls = tryClass()

method = getattr(my_cls, funcName)
method(input[1], input[2])

如果我输入 callFunction hello world 它可以工作:)

【讨论】:

    【解决方案2】:

    如果没有任何参数包含空格,你可以这样做

    fn_args=cmd.split()
    python_code="%s(%s)" % (fn[0], ", ".join(fn_args[1:]))
    eval(python_code)
    

    编辑:

    如果不是那么简单,你应该看看https://docs.python.org/3/library/cmd.htmlhttps://docs.python.org/3/library/argparse.html,但是在执行任意代码之前需要做一些准备

    编辑2:

    如果你不需要你的 args 是精确的 python,你可以将它们解析为 json with the standard library

    你可以这样做

    import json
    cmd='fname "a" "b" 1'
    fn,sep,args=cmd.strip().partition(" ")
    end=0
    largs=[]
    d=json.JSONDecoder()
    while end < len(args):
        args=args[end:].strip()
        arg,end=d.raw_decode(args)
        largs.append(arg)
    exec(fn)(*largs) # better look into exec docs
    

    【讨论】:

    • 其实会有空格和逗号。
    • @ZhipengYANG 为不那么简单的案例添加了链接
    • @ZhipengYANG 添加了 JSON hack
    【解决方案3】:

    内置的shlex 模块可能就是你想要的:

    >>> import shlex
    >>> cmd = "func arg0 arg1 'arg2 has spaces'"
    >>> list(shlex.shlex(cmd))
    ['func', 'arg0', 'arg1', "'arg2 has spaces'"]
    

    如果您可以信任输入,那么实际调用它的方式如下:

    >>> tokens = list(shlex.shlex(cmd))
    >>> # here is a stupid func function that reverses its input args
    >>> func = lambda *args: print(*reversed(args))
    >>> eval(tokens[0])(*tokens[1:])
    'arg2 has spaces' arg1 arg0
    

    【讨论】:

      【解决方案4】:

      我想要的只是一个简单的分词器。我想通过调用eval() 来运行函数。这就是我为我的项目所做的。

      结果如下:

      >>> tokenizer('func 123 abc')
      [('func', 'func'), ('arg', '123'), ('arg', 'abc')]
      >>> tokenizer('func 123.5 abc')
      [('func', 'func'), ('arg', '123.5'), ('arg', 'abc')]
      >>> tokenizer('func 123.5 abc "Hello, World!"')
      [('func', 'func'), ('arg', '123.5'), ('arg', 'abc'), ('arg', 'Hello, World!')]
      >>> tokenizer("func 123.5 abc 'Hello, World!'")
      [('func', 'func'), ('arg', '123.5'), ('arg', 'abc'), ('arg', 'Hello, World!')]
      

      注意:这可能并不适合所有人,这不是一个完整的解析器或标记器。

      代码:

      def isNumber(cmd):
          try:
              int(cmd)
              return True
          except ValueError:
              try:
                  float(cmd)
                  return True
              except ValueError:
                  return False
          return False
      
      def isWord(cmd):
          if len(cmd) == 0:
              return False
          if cmd[0].isalpha():
              for i in cmd[1:]:
                  if not i.isalpha() and i != '_' and i != '-':
                      return False
              return True
          return False
      def spaceParser(cmd):
          i = 0
          for i in range(len(cmd)):
              if cmd[i] == ' ':
                  continue
              break
          return cmd[i:]
      
      def funcNameParser(cmd):
          cmd = spaceParser(cmd)
          i = 0
          word = ''
          for i in range(len(cmd)):
              if cmd[i] != ' ':
                  word += cmd[i]
              else:
                  break
          if i + 1 > len(word):
              return (word, cmd[i:])
          return (word, cmd[i+1:])
      
      def argumentParser(cmd):
          cmd = spaceParser(cmd)
          if cmd[0] == '\'':
              word = ''
              i = 0
              for i in range(1, len(cmd)):
                  if cmd[i] != '\'':
                      word += cmd[i]
                  else:
                      return (word, cmd[i+1:])
              assert False, 'Fatal exception: String not finished.'
          if cmd[0] == '"':
              word = ''
              i = 0
              for i in range(1, len(cmd)):
                  if cmd[i] != '"':
                      word += cmd[i]
                  else:
                      return (word, cmd[i+1:])
              assert False, 'Fatal exception: String not finished.'            
          i = 0
          word = ''
          for i in range(len(cmd)):
              if cmd[i] != ' ':
                  word += cmd[i]
              else:
                  break
          assert isWord(word) or isNumber(word), 'Fatal exception: Not a valid name.'
          if i + 1 > len(word):
              return (word, cmd[i:]) 
          return (word, cmd[i+1:])
      
      def tokenizer(cmd):
          token = []
          result = funcNameParser(cmd)
          token += [('func', result[0])]
          while len(result[1]) != 0:
              result = argumentParser(result[1])
              token += [('arg', result[0])]
          return token
      

      【讨论】:

        猜你喜欢
        • 2021-04-25
        • 1970-01-01
        • 2013-04-02
        • 2019-08-29
        • 1970-01-01
        • 2015-09-11
        • 2022-11-16
        • 2017-04-15
        • 2011-04-26
        相关资源
        最近更新 更多