- html - 出于某种原因,IE8 对我的 Sass 文件中继承的 html5 CSS 不友好?
- JMeter 在响应断言中使用 span 标签的问题
- html - 在 :hover and :active? 上具有不同效果的 CSS 动画
- html - 相对于居中的 html 内容固定的 CSS 重复背景?
问题最初在 code review 上提出。通过推荐再次在这里询问。
<小时/>一个forcefield是用于计算复杂系统势能的函数和参数的集合。我有文本文件,其中包含有关力场参数的数据。文本文件分为许多部分,每个部分都遵循相同的格式:
indices:
后跟一个整数列表。 这是一个虚构的示例文件来展示该格式。
############################################
# Comments begin with '#'
############################################
[lj_pairs] # Section 1
indices: 0 2
# ID eps sigma
1 2.344 1.234 5
2 4.423 5.313 5
3 1.573 6.321 5
4 1.921 11.93 5
[bonds]
indices: 0 1
2 4.234e-03 11.2
6 -0.134545 5.7
目标是解析此类文件并将所有信息存储在 dict
中。
目前,我有以下代码来完成我的任务
""" Force-field data reader """
import re
from dataclasses import dataclass, field
from typing import Dict, Iterable, List, TextIO, Tuple, Union, Any
def ff_reader(fname: Union[str, TextIO]) -> Dict[str, "FFSections"]:
""" Reads data from a force-field file """
try:
if _is_string(fname):
fh = open(fname, mode="r")
own = True
else:
fh = iter(fname)
except TypeError:
raise ValueError("fname must be a string or a file handle")
# All the possible section headers
keywords = ("lj_pairs", "bonds") # etc... Long list of possible sections
# Removed for brevity
re_sections = re.compile(r"^\[(%s)\]$" % "|".join(keywords))
ff_data = _strip_comments(fh)
# Empty dict that'll hold all the data.
final_ff_data = {key: FFSections() for key in keywords}
# Get first section header
for line in ff_data:
match = re.match(re_sections, line)
if match:
section = match.group(1)
in_section_for_first_time = True
break
else:
raise FFReaderError("A valid section header must be the first line in file")
else:
raise FFReaderError("No force-field sections exist")
# Read the rest of the file
for line in ff_data:
match = re.match(re_sections, line)
# If we've encounted a section header the next line must be an index list.
if in_section_for_first_time:
if line.split()[0] != "indices:":
raise FFReaderError(f"Missing index list for section: {section}")
idx = _validate_indices(line)
final_ff_data[section].use_idx = idx
in_section_for_first_time = False
in_params_for_first_time = True
continue
if match and in_params_for_first_time:
raise FFReaderError(
f"Section {section} missing parameters"
+ "Sections must contain atleast one type coefficients"
)
if match: # and not in_section_for_first_time and in_params_for_first_time
section = match.group(1)
in_section_for_first_time = True
continue
params = _validate_params(line)
final_ff_data[section].coeffs.update([params])
in_params_for_first_time = False
# Close the file if we opened it
if own:
fh.close()
for section in final_ff_data.values():
# coeff must exist if use_idx does
if section.use_idx is not None:
assert section.coeffs
return final_ff_data
def _strip_comments(
instream: TextIO, comments: Union[str, Iterable[str], None] = "#"
) -> Iterable[str]:
""" Strip comments from a text IO stream """
if comments is not None:
if isinstance(comments, str):
comments = [comments]
comments_re = re.compile("|".join(map(re.escape, comments)))
else:
comments_re = ".*"
try:
for lines in instream.readlines():
line = re.split(comments_re, lines, 1)[0].strip()
if line != "":
yield line
except AttributeError:
raise TypeError("instream must be a `TextIO` stream") from None
@dataclass(eq=False)
class FFSections:
"""
FFSections(coeffs,use_idx)
Container for forcefield information
"""
coeffs: Dict[int, List[float]] = field(default_factory=dict)
use_idx: List[int] = field(default=None)
class FFReaderError(Exception):
""" Incorrect or badly formatted force-Field data """
def __init__(self, message: str, badline: Optional[str] = None) -> None:
if badline:
message = f"{message}\nError parsing --> ({badline})"
super().__init__(message)
def _validate_indices(line: str) -> List[int]:
"""
Check if given line contains only a whitespace separated
list of integers
"""
# split on indices: followed by whitescape
split = line.split("indices:")[1].split()
# import ipdb; ipdb.set_trace()
if not set(s.isdecimal() for s in split) == {True}:
raise FFReaderError(
"Indices should be integers and seperated by whitespace", line
)
return [int(x) for x in split]
def _validate_params(line: str) -> Tuple[int, List[float]]:
"""
Check if given line is valid param line, which are
an integer followed by one or more floats seperated by whitespace
"""
split = line.split()
id_ = split[0]
coeffs = split[1:]
if not id_.isdecimal():
raise FFReaderError("Invalid params", line)
try:
coeffs = [float(x) for x in coeffs]
except (TypeError, ValueError):
raise FFReaderError("Invalid params", line) from None
return (int(id_), coeffs)
<小时/>
这似乎需要很多代码来完成一个简单的任务。如何使用 parsimonious
或类似的解析库来简化解析此类文件?
最佳答案
正如另一个答案中所述,您可以使用解析库,例如 parsimonious
与 NodeVisitor
结合使用类:
from parsimonious.grammar import Grammar
from parsimonious.nodes import NodeVisitor
data = """
############################################
# Comments begin with '#'
############################################
[lj_pairs] # Section 1
indices: 0 2
# ID eps sigma
1 2.344 1.234 5
2 4.423 5.313 5
3 1.573 6.321 5
4 1.921 11.93 5
[bonds]
indices: 0 1
2 4.234e-03 11.2
6 -0.134545 5.7
"""
grammar = Grammar(
r"""
expr = (entry / garbage)+
entry = section garbage indices (valueline / garbage)*
section = lpar word rpar
indices = ws? "indices:" values+
garbage = ((comment / hs)* newline?)*
word = ~"\w+"
values = number+
valueline = values newline?
number = hs? ~"[-.e\d]+" hs?
lpar = "["
rpar = "]"
comment = ~"#.+"
ws = ~"\s*"
hs = ~"[\t\ ]*"
newline = ~"[\r\n]"
"""
)
tree = grammar.parse(data)
class DataVisitor(NodeVisitor):
def visit_number(self, node, visited_children):
""" Returns integer and float values. """
_, value, _ = visited_children
try:
number = int(value.text)
except ValueError:
number = float(value.text)
return number
def visit_section(self, node, visited_children):
""" Returns the section as text. """
_, section, _ = visited_children
return section.text
def visit_indices(self, node, visited_children):
""" Returns the index numbers. """
*_, values = visited_children
return values[0]
def visit_valueline(self, node, visited_children):
""" Returns every value from one line. """
values, _ = visited_children
return values
def visit_entry(self, node, visited_children):
""" Returns one entry (section, indices, values). """
section, _, indices, lst = visited_children
values = [item[0] for item in lst if item[0]]
return (section, {'indices': indices, 'values': values})
def visit_expr(self, node, visited_children):
""" Returns the whole structure as a dict. """
return dict([item[0] for item in visited_children if item[0]])
def visit_garbage(self, node, visited_children):
""" You know what this does. """
return None
def generic_visit(self, node, visited_children):
""" Returns the visited children (if any) or the node itself. """
return visited_children or node
d = DataVisitor()
result = d.visit(tree)
print(result)
这将产生
{
'lj_pairs': {'indices': [0, 2], 'values': [[1, 2.344, 1.234, 5], [2, 4.423, 5.313, 5], [3, 1.573, 6.321, 5], [4, 1.921, 11.93, 5]]},
'bonds': {'indices': [0, 1], 'values': [[2, 0.004234, 11.2], [6, -0.134545, 5.7]]}
}
您的原始数据文件可以被视为 DSL
- 域s特定l语言。因此,我们需要一个语法来描述您的格式允许的样子。这里通常的方法是首先构建小块,例如空格或“单词”。
parsimonious
我们有几个选项,其中一个是指定正则表达式(以
~
开头):
ws = ~"\s*"
在这里,ws
代表\s*
这是零个或多个空格。
lpar = "["
<小时/>最后也是最强大的可能性是将这两个较小的部分
组合以形成一个更大的部分,例如
section = lpar word rpar
翻译为 [word_characters_HERE123]
或类似的结构。
/
)和量词,例如
*
(零矿多,贪心),
+
(多一矿石,贪心)和
?
(零或一,贪婪)并且可以放在我们可能想到的每个表达式之后。
dict
),我们需要将它输入到 NodeVisitor
中。类(class)。这是我们之前形成的语法的附属方法 visit_*
会调用每一片适合它的叶子。就是说一个方法visit_section(...)
将在每个 section
上被调用叶与其适当的visited_children
。 让我们更清楚地说明这一点。函数
def visit_section(self, node, visited_children):
""" Returns the section as text. """
_, section, _ = visited_children
return section.text
将调用 section
我们语法的一部分( lpar section rpar
),所以叶子 section
有这三个 child 。我们对 [
都不感兴趣也不是 ]
但只有部分文本本身,所以我们进行一些解包并返回 section.text
。
我们需要对之前定义的每个节点/叶子执行此操作。默认情况下,第一个定义(在我们的例子中为 expr
)和相应的 visit_expr(...)
将是 NodeVisitor
的输出class 和所有其他节点都是该节点的子节点(孙子节点、曾孙节点等)。
关于python - 使用 parsimonious 解析势函数的参数,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/56011353/
简而言之:我想从可变参数模板参数中提取各种选项,但不仅通过标签而且通过那些参数的索引,这些参数是未知的 标签。我喜欢 boost 中的方法(例如 heap 或 lockfree 策略),但想让它与 S
我可以对单元格中的 excel IF 语句提供一些帮助吗? 它在做什么? 对“BaselineAmount”进行了哪些评估? =IF(BaselineAmount, (Variance/Baselin
我正在使用以下方法: public async Task Save(Foo foo,out int param) { ....... MySqlParameter prmparamID
我正在使用 CodeGear RAD Studio IDE。 为了使用命令行参数测试我的应用程序,我多次使用了“运行 -> 参数”菜单中的“参数”字段。 但是每次我给它提供一个新值时,它都无法从“下拉
我已经为信用卡类编写了一些代码,粘贴在下面。我有一个接受上述变量的构造函数,并且正在研究一些方法将这些变量格式化为字符串,以便最终输出将类似于 号码:1234 5678 9012 3456 截止日期:
MySql IN 参数 - 在存储过程中使用时,VarChar IN 参数 val 是否需要单引号? 我已经像平常一样创建了经典 ASP 代码,但我没有更新该列。 我需要引用 VarChar 参数吗?
给出了下面的开始,但似乎不知道如何完成它。本质上,如果我调用 myTest([one, Two, Three], 2); 它应该返回元素 third。必须使用for循环来找到我的解决方案。 funct
将 1113355579999 作为参数传递时,该值在函数内部变为 959050335。 调用(main.c): printf("%d\n", FindCommonDigit(111335557999
这个问题在这里已经有了答案: Is Java "pass-by-reference" or "pass-by-value"? (92 个回答) 关闭9年前。 public class StackOve
我真的很困惑,当像 1 == scanf("%lg", &entry) 交换为 scanf("%lg", &entry) == 1 没有区别。我的实验书上说的是前者,而我觉得后者是可以理解的。 1 =
我正在尝试使用调用 SetupDiGetDeviceRegistryProperty 的函数使用德尔福 7。该调用来自示例函数 SetupEnumAvailableComPorts .它看起来像这样:
我需要在现有项目上实现一些事件的显示。我无法更改数据库结构。 在我的 Controller 中,我(从 ajax 请求)传递了一个时间戳,并且我需要显示之前的 8 个事件。因此,如果时间戳是(转换后)
rails 新手。按照多态关联的教程,我遇到了这个以在create 和destroy 中设置@client。 @client = Client.find(params[:client_id] || p
通过将 VM 参数设置为 -Xmx1024m,我能够通过 Eclipse 运行 Java 程序-Xms256M。现在我想通过 Windows 中的 .bat 文件运行相同的 Java 程序 (jar)
我有一个 Delphi DLL,它在被 Delphi 应用程序调用时工作并导出声明为的方法: Procedure ProduceOutput(request,inputs:widestring; va
浏览完文档和示例后,我还没有弄清楚 schema.yaml 文件中的参数到底用在哪里。 在此处使用 AWS 代码示例:https://github.com/aws-samples/aws-proton
程序参数: procedure get_user_profile ( i_attuid in ras_user.attuid%type, i_data_group in data_g
我有一个字符串作为参数传递给我的存储过程。 dim AgentString as String = " 'test1', 'test2', 'test3' " 我想在 IN 中使用该参数声明。 AND
这个问题已经有答案了: When should I use "this" in a class? (17 个回答) 已关闭 6 年前。 我运行了一些java代码,我看到了一些我不太明白的东西。为什么下
我输入 scroll(0,10,200,10);但是当它运行时,它会传递字符串“xxpos”或“yypos”,我确实在没有撇号的情况下尝试过,但它就是行不通。 scroll = function(xp
我是一名优秀的程序员,十分优秀!