我正在为一家小型网络托管公司工作,并决定编写一个脚本来对我们相当大的命名/ bind9区域配置文件进行排序。我对它的结果有点满意(至少它有效),但是中心解析功能的不雅使我感到困扰。作为参考,典型的区域定义如下所示(在从属服务器上。主服务器看起来更简单):
zone "somewebsite.com" {
masters { ip.ad.dr.ess; };
type slave;
allow-query { any; };
file "slave/db.somewebsite.com";
};
该文件中约有190个。我需要的每个区域是站点名称(用作排序键)和包含区域的整个字符串。所以这里是我的解析器(以及一个用于保存区域名称及其全文的微小数据类型):
type SortKey = String
type ZoneText = String
data Zone = Zone SortKey ZoneText deriving Show
allZonesParser :: Parser [Zone]
allZonesParser = do zones <- many zoneParser
return zones
zoneParser :: Parser Zone
zoneParser = do p1 <- string "zone"
p2 <- many space
p3 <- string "\""
zoneName <- many (alphaNum <|> oneOf ".-")
p4 <- string "\""
p5 <- many space
p6 <- manyTill anyChar (try (string ";" >> newline >> string "};"))
p7 <- many space
p8 <- many newline
return $ Zone zoneName (p1 ++ p2 ++ p3 ++ zoneName ++ p4 ++ p5 ++ p6 ++ ";\n};" ++ p7 ++ p8)
我意识到这个解析器不适用于所有用例,但对于我们的区域配置,它足够先进。它会抓取整个区域部分,直到找到;\n};
,然后重建区域文本。我的主要抱怨是:我无法弄清楚如何保留代表区域的整个字符串而不使用9个monadic绑定,然后将它们与++
运算符拼接在一起。是否有一种优雅的方式来消耗所有这些输入并保留/使用已解析的所有内容?我需要稍后使用解析后的字符串来编写一个新的排序区域配置文件,并且重建&#34;这似乎很荒谬。字符串我在这里做的方式。我已经阅读了Parsec documentation的很大一部分而没有找到合适的方法将它拼凑在一起。
我的完整代码是here。我建议不要使用它,除非你修改它以适应你的区域配置间隔和换行的方式。
答案 0 :(得分:1)
使用Parsec你当然可以做得更短,这是我提出来的
zoneParse = do
string "zone"
space1 <- many space
zoneName <- between (char '"') (char '"') (many $ noneOf "\"")
body <- manyTill anyChar (try $ string ";\n};")
return $ Zone zoneName $ concat ["zone", space1, "\"", zoneName, "\"", body, ";\n};"]
在这里,我减少了要执行的绑定数量,因为其中一些只是捕获字符串文字,可以稍后手动插入。我还使用between
来捕获zoneName
,因为它在Parsec中是一个非常方便的组合器。之后,它只是解析所有字符,直到;\n};
被发现(如果您的文件中没有string ";" >> newline >> string "};"
,则与\r\n
相同,否则请坚持使用newline
{1}}),然后使用concat