需要一个简单的预处理器来处理文本文件
我需要把一个文本文件里的简单表达式去掉,然后保存到一个新文件里。用什么表达式语言都可以。举个例子:
输入文件:
days = 12
times_per_day = 10
extra = 2
The quick brown fox jumped $days * ($times_per_day + $extra) times over the lazy dog
输出文件:
The quick brown fox jumped 144 times over the lazy dog
这个程序需要在Windows上运行。我考虑过的一些方法是:使用C语言的预处理器(不过我觉得它可能无法静态地处理所有表达式?),或者用perl/python/awk等语言来写我的输入文件(但这样可能不太好读)。
我希望在3到4个小时内解决这个问题,所以我不想写一个完整的解析器。
5 个回答
如果你把上面那些表达式的评估和替换分开做,会简单很多。在一句话中找到一个要评估的表达式其实挺麻烦的,不过在=
右边做这个就简单多了。所以像下面这样的操作,几分钟就能搞定,而且仍然可以用来构建一个非常复杂的语言。
data = '''days = 12
times_per_day = 10
extra = 2
total = $days * ($times_per_day + $extra)
The quick brown fox jumped $total times over the lazy dog
This is a new line that does nothing
The next line will reassign the variable total
total = $total + 1
Now the value of total is $total
'''.split('\n')
variables = {}
def replaceVariables(l):
for k in variables: l = l.replace(k, variables[k])
return l
def evalExpression(l): return str( eval( replaceVariables(l) ) )
for l in data:
# if its a variable assignment, create a new variable
# this is also going to replace an old variable.
if '=' in l:
v, d = map(lambda m: m.strip(), l.split('='))
variables['$' + v] = evalExpression(d)
continue
# Otherwise just replace variables
print replaceVariables(l)
对于上面的内容,结果是:
In [13]: run test18
The quick brown fox jumped 144 times over the lazy dog
This is a new line that does nothing
The next line will reassign the variable total
Now the value of total is 145
下面是用PHP写的代码示例:
<?
$days = 12;
$times_per_day = 10;
$extra = 2;
?>
The quick brown fox jumped <? echo $days * ($times_per_day + $extra); ?> times over the lazy dog
在这个输入下,PHP会产生以下结果:
The quick brown fox jumped 144 times over the lazy dog
我唯一不喜欢的就是需要使用echo
这个命令(还有在Windows上安装PHP的难度)。
在Python中,我会使用一个模板引擎。比如说Jinja2。代码如下:
from jinja2 import Environment, FileSystemLoader, Template
def main():
environment = Environment(loader=FileSystemLoader('.'), trim_blocks=True)
template = environment.get_template('test.tpl')
print template.render()
if __name__ == '__main__':
main()
模板文件 test.tpl
:
{% set days = 12 %}
{% set times_per_day = 10 %}
{% set extra = 2 %}
The quick brown fox jumped {{days * (times_per_day + extra)}} times over the lazy dog.
输出结果:
The quick brown fox jumped 144 times over the lazy dog.
如果你打算使用 AWK,那为什么 GNU Bash 不合适呢?可以这样理解:
#!/bin/bash
days=12
times_per_day=10
extra=2
cat << EOF
The quick brown fox jumped $((days * (times_per_day + extra))) times over the lazy dog
EOF
希望这个例子能让你看得懂。
如果你出于某种原因想要不使用 cat
(这是一个外部工具),那也没问题:
while read; do printf '%s\n' "$REPLY"; done << EOF
The quick brown fox jumped $((days * (times_per_day + extra))) times over the lazy dog
EOF
每次使用 eval
的时候,几乎可以说是在搞一些小聪明。
不过,下面这个 perl 的“一行代码”在这个特定的情况下确实能完成任务:
perl -ne '!eof() ? eval "\$$_" : s/(?<!\S)(?=[[:punct:]\d]*\$)((?:\$\w+|[[:punct:]\d]+|\s+)+)(?!\S)/\@{[$1]}/g && print eval qq{"$_"};' fox.txt
输出结果:
The quick brown fox jumped 144 times over the lazy dog
解释:
开关选项:
-n
: 为输入文件的每一行创建一个while(<>){...}
循环。-e
: 告诉perl
在命令行上执行代码。
代码说明:
!eof()
: 根据是否到达文件末尾来选择性处理。eval "\$$_"
: 将一行像foo = 3
的内容转化为$foo = 3
。s/(?<!\S)(?=[[:punct:]\d]*\$)((?:\$\w+|[[:punct:]\d]+|\s+)+)(?!\S)/\@{[$1]}/g
把像
string $foo * $bar end
的文本转化为string @{[$foo * $bar]} end
。print eval qq{"$_"};
: 打印最后一行的计算结果。