正则表达式 - 删除 space 连续多个字符,但排除所有注释掉的行
Regex - Remove space chars whenever there are more than one in succession, but exclude all lines commented out
假设我有几行如下:
01090 C -------CALCULATION OF SOMETHING--
01100 "SOME.VARIABLE" = "SOME.OTHER.VARIABLE" + 2
01110 IF("SOME.VARIABLE" .NE. "SOME.VALUE") THEN ON("SOME.MACHINE")
我想通过程序删除所有 space 个以上连续的字符。例如,第 01100 行在“=”之前有三 (3) 个 space 个字符,在“=”之后有两 (2) 个字符。在第 01110 行中,有几个不同的位置具有超过 1 个连续的 space 个字符。
我想用一个 space 字符替换它们。我不想 remove/alter 注释行 01090 中包含的 space。
所有行都以 5 位数字开头,所有行的行号后面都有一个制表符,只有注释行有“C”或“c”表示它们已被注释掉。
我正在使用 Sublime3 和 boost 正则表达式。我尝试过类似的东西:
(?!\t[Cc] )[ ]{2,}
(?!\t[Cc])[ ]{2,}
我似乎无法确定如何在不捕获整行的情况下否定整行。
我也试过在开头放一个插入符号,但这似乎没有帮助。
基本上,如果该行有一个“TAB”后跟一个“c”或“C”,则忽略整个内容。否则,任何两个或多个连续的 space 字符被定位并替换为单个 space 字符。
编辑
--------解决方案--------
感谢 Wiktor 和 The fourth bird 的输入,我能够确定解决方案。非常感谢他们。这是我最终得到的结果:
^\d+\t[cC].*\K|[ ]{2,}
我还确定,如果行尾有额外的 space,我可能也想忽略它们,以便我可以使用不同的正则表达式搜索完全删除它们。最终产品如下所示:
^\d+\t[cC].*\K|[ ]*\n\K|[ ]{2,}
如果不是受限于boost或者PCRE的引擎,我相信我之前失败的一次尝试真的能成功。如果它能帮助别人,我会把它包括在这里。它不能用于 boost 或 PCRE,因为它们不支持无限后视:
(?<!\t[cC].*)[ ]{2,}
您必须向您的正则表达式添加否定前瞻和否定后视。尝试这样的事情。
(?<![Cc])\s{3,}(?![Cc])
您可能会使用
^\d{5}\t[cC] .*$(*SKIP)(*FAIL)|\h{2,}
^
字符串开头
\d{5}\t
匹配 5 个数字和一个制表符
[cC]
匹配 c
或 C
和 space
.*$
匹配行的其余部分
(*SKIP)(*FAIL)
跳过比赛
|
或
\h{2,}
匹配 2 个或更多水平白色space 个字符
在替换中使用单个 space。
正如 OP 所解决的,使用 \K
并匹配 2 个或更多 spaces 的缩短版本:
^\d+\tC.*\K|[ ]{2,}
^
字符串开头
\d+
匹配1个或多个数字
\tC
匹配制表符和 C
字符
.*\K
匹配行的其余部分并清除匹配缓冲区
|
或
[ ]{2,}
匹配2个或更多space(方括号只是为了可见,不是必需的)
我认为您实际上可能正在准备解析这种语言。解析器通常不方便使用正则表达式。
此外,您没有询问,但在这种情况下,转换可能是 in-place(因为输出比输入短,或者长度相等)。
我建议使用这样的 PEG 语法(使用 Boost Spirit):
template <typename In, typename Out>
Out compress_whitespace(In f, In l, Out out) {
auto copy = [&out](auto& ctx) {
struct Append {
static void call(Out& out, char ch) { *out++ = ch; }
static void call(Out& out, boost::iterator_range<In> raw) {
for (auto ch : raw) *out++ = ch; }
};
Append::call(out, _attr(ctx));
};
using namespace boost::spirit::x3;
auto prefix = raw[uint_ >> " "][copy];
auto comment = raw["C " >> *(char_ - eol)][copy];
auto code_ch = omit[+blank] >> attr(' ')[copy] | (char_ - eol)[copy];
auto line = prefix >> (comment | *code_ch);
auto newline = raw[eol][copy];
parse(f, l, -line % newline);
return out;
}
禁止空行:
parse(f, l, line % newline);
要抛出 incomplete/invalid 输入,请更改 parse
行:
parse(f, l, expect[line % newline >> *newline >> eoi]);
int main(int argc, char** argv)
{
std::ostreambuf_iterator out(std::cout);
for (std::string file : std::vector(argv+1, argv+argc)) {
std::ifstream s(file, std::ios::binary);
std::string const program(std::istreambuf_iterator<char>{s}, {});
compress_whitespace(begin(program), end(program), out);
}
}
输出使用vim -d input.txt <(./sotest input.txt)
:
奖励:就地处理
由于我们知道输出的长度相同或更短,因此您可以就地处理:
std::string program = R"~(
01090 C -------CALCULATION OF SOMETHING--
01100 "SOME.VARIABLE" = "SOME.OTHER.VARIABLE" + 2
01110 IF("SOME.VARIABLE" .NE. "SOME.VALUE") THEN ON("SOME.MACHINE"))~";
auto b = begin(program), e = end(program),
new_e = compress_whitespace(b, e, b);
std::cout << "Shorter by " << (e - new_e) << " chars\n";
program.erase(new_e, e);
std::cout << program << "\n";
看到了Live On Coliru,打印:
Shorter by 7 chars
01090 C -------CALCULATION OF SOMETHING--
01100 "SOME.VARIABLE" = "SOME.OTHER.VARIABLE" + 2
01110 IF("SOME.VARIABLE" .NE. "SOME.VALUE") THEN ON("SOME.MACHINE")
假设我有几行如下:
01090 C -------CALCULATION OF SOMETHING--
01100 "SOME.VARIABLE" = "SOME.OTHER.VARIABLE" + 2
01110 IF("SOME.VARIABLE" .NE. "SOME.VALUE") THEN ON("SOME.MACHINE")
我想通过程序删除所有 space 个以上连续的字符。例如,第 01100 行在“=”之前有三 (3) 个 space 个字符,在“=”之后有两 (2) 个字符。在第 01110 行中,有几个不同的位置具有超过 1 个连续的 space 个字符。 我想用一个 space 字符替换它们。我不想 remove/alter 注释行 01090 中包含的 space。
所有行都以 5 位数字开头,所有行的行号后面都有一个制表符,只有注释行有“C”或“c”表示它们已被注释掉。
我正在使用 Sublime3 和 boost 正则表达式。我尝试过类似的东西:
(?!\t[Cc] )[ ]{2,}
(?!\t[Cc])[ ]{2,}
我似乎无法确定如何在不捕获整行的情况下否定整行。
我也试过在开头放一个插入符号,但这似乎没有帮助。
基本上,如果该行有一个“TAB”后跟一个“c”或“C”,则忽略整个内容。否则,任何两个或多个连续的 space 字符被定位并替换为单个 space 字符。
编辑
--------解决方案--------
感谢 Wiktor 和 The fourth bird 的输入,我能够确定解决方案。非常感谢他们。这是我最终得到的结果:
^\d+\t[cC].*\K|[ ]{2,}
我还确定,如果行尾有额外的 space,我可能也想忽略它们,以便我可以使用不同的正则表达式搜索完全删除它们。最终产品如下所示:
^\d+\t[cC].*\K|[ ]*\n\K|[ ]{2,}
如果不是受限于boost或者PCRE的引擎,我相信我之前失败的一次尝试真的能成功。如果它能帮助别人,我会把它包括在这里。它不能用于 boost 或 PCRE,因为它们不支持无限后视:
(?<!\t[cC].*)[ ]{2,}
您必须向您的正则表达式添加否定前瞻和否定后视。尝试这样的事情。
(?<![Cc])\s{3,}(?![Cc])
您可能会使用
^\d{5}\t[cC] .*$(*SKIP)(*FAIL)|\h{2,}
^
字符串开头\d{5}\t
匹配 5 个数字和一个制表符[cC]
匹配c
或C
和 space.*$
匹配行的其余部分(*SKIP)(*FAIL)
跳过比赛|
或\h{2,}
匹配 2 个或更多水平白色space 个字符
在替换中使用单个 space。
正如 OP 所解决的,使用 \K
并匹配 2 个或更多 spaces 的缩短版本:
^\d+\tC.*\K|[ ]{2,}
^
字符串开头\d+
匹配1个或多个数字\tC
匹配制表符和C
字符.*\K
匹配行的其余部分并清除匹配缓冲区|
或[ ]{2,}
匹配2个或更多space(方括号只是为了可见,不是必需的)
我认为您实际上可能正在准备解析这种语言。解析器通常不方便使用正则表达式。
此外,您没有询问,但在这种情况下,转换可能是 in-place(因为输出比输入短,或者长度相等)。
我建议使用这样的 PEG 语法(使用 Boost Spirit):
template <typename In, typename Out>
Out compress_whitespace(In f, In l, Out out) {
auto copy = [&out](auto& ctx) {
struct Append {
static void call(Out& out, char ch) { *out++ = ch; }
static void call(Out& out, boost::iterator_range<In> raw) {
for (auto ch : raw) *out++ = ch; }
};
Append::call(out, _attr(ctx));
};
using namespace boost::spirit::x3;
auto prefix = raw[uint_ >> " "][copy];
auto comment = raw["C " >> *(char_ - eol)][copy];
auto code_ch = omit[+blank] >> attr(' ')[copy] | (char_ - eol)[copy];
auto line = prefix >> (comment | *code_ch);
auto newline = raw[eol][copy];
parse(f, l, -line % newline);
return out;
}
禁止空行:
parse(f, l, line % newline);
要抛出 incomplete/invalid 输入,请更改 parse
行:
parse(f, l, expect[line % newline >> *newline >> eoi]);
int main(int argc, char** argv)
{
std::ostreambuf_iterator out(std::cout);
for (std::string file : std::vector(argv+1, argv+argc)) {
std::ifstream s(file, std::ios::binary);
std::string const program(std::istreambuf_iterator<char>{s}, {});
compress_whitespace(begin(program), end(program), out);
}
}
输出使用vim -d input.txt <(./sotest input.txt)
:
奖励:就地处理
由于我们知道输出的长度相同或更短,因此您可以就地处理:
std::string program = R"~(
01090 C -------CALCULATION OF SOMETHING--
01100 "SOME.VARIABLE" = "SOME.OTHER.VARIABLE" + 2
01110 IF("SOME.VARIABLE" .NE. "SOME.VALUE") THEN ON("SOME.MACHINE"))~";
auto b = begin(program), e = end(program),
new_e = compress_whitespace(b, e, b);
std::cout << "Shorter by " << (e - new_e) << " chars\n";
program.erase(new_e, e);
std::cout << program << "\n";
看到了Live On Coliru,打印:
Shorter by 7 chars
01090 C -------CALCULATION OF SOMETHING--
01100 "SOME.VARIABLE" = "SOME.OTHER.VARIABLE" + 2
01110 IF("SOME.VARIABLE" .NE. "SOME.VALUE") THEN ON("SOME.MACHINE")