awk sed backreference csv文件

时间:2018-12-15 09:01:38

标签: bash awk sed

扩展前一个here的问题。 (我喜欢问新问题,而不是编辑第一个问题。我可能是错的)

编辑:好的,我错了,我应该编辑第一个问题。我不好(所以问题是一门艺术,很难掌握)

我有一个csv文件,用半列作为字段分隔符。这是csv文件的摘录:

...;field;(:);10000(n,d);(:);field;.... ...;field;123.12(b);123(a);123.00(:);....

这是所需的输出:

...;field;(:);(n,d) 10000;(:);field;.... ...;field;(b) 123.12;(a) 123;(:) 123.00;....

我正在寻找一种解决方案,以便在每个字段中交换2种模式。

模式1:任意数字,带有可选的小数点(。)和可选的十进制数字

例如:1 / 1111.00 / 444444444.3 / 32 / 32.6666666 / 1.0 / ....

模式2:任何以左括号开头,后接一个或多个字符,以右括号结尾的字符串

例如:(n,a,p)/(:) /(llll)/(d)/(123)/(1; 2; 3)...

第一个问题中提供的解决方案适用于仅包含一列的简单文件。如果我在csv文件中尝试该解决方案,则会遇到多个故障。

因此,我尝试awk类似的解决方案,(我认为)该解决方案更“面向列”。

我尝试过

awk -F";" '{print gensub(/([[:digit:].]*)(\(.*\))/, "\\2 \\1", "g")}' file

尽管我通过固定字段定界符(;),但“我的正则表达式交换”将在每个字段中成功。这是一个错误。

这是失败的一个例子

;(:);7320000(n,d);(:)

所需的输出-> ;(:);(n,d) 7320000;(:)

我的问题(最后):awk在单列文件成功时为何失败。面对挑战的最佳工具是什么?

  1. 使用了很长的正则表达式吗?
  2. 正则表达式很长吗?
  3. for循环?
  4. 其他工具?

PS:我知道我不清楚。我有2个问题(英语,技术限制)。抱歉。

3 个答案:

答案 0 :(得分:0)

好吧,当解析不带引号的简单分隔的文件时,通常awk可以解决:

awk -vFS=';' -vOFS=';' '{
    for (i = 1; i < NF; i++) {
        split($i, t, "(")
        if (length(t[1]) != 0 && length(t[2]) != 0) {
            $i="("t[2]" "t[1]
        }
    }
    print
}' <<EOF
...;field;(:);10000(n,d);(:);field;....
...;field;123.12(b);123(a);123.00(:);....
EOF

但是,如果用引号括起来,则此操作将失败。分隔符;位于值内...

  1. 首先,我们将输入和输出seapartor设置为;
  2. 我们迭代for (i = 1; i < NF; i++)行中的所有字段
  3. 我们将行分隔为(个字符
  4. 如果分割为(的第一个字段的长度为非零,而第二个字段的长度也为非零
  5. 我们将这些字段交换为Fireld,并添加一个空格(我们还记得开始时删除的()。
  6. 然后行print被编辑。

使用sedxargs的解决方案,但您需要提前知道字段数:

{
    sed 's/;/\n/g' |
    sed 's/\([^(]\{1,\}\)\((.*)\)/\2 \1/' |
    xargs -d '\n' -n7 -- printf "%s;%s;%s;%s;%s;%s;%s\n"
} <<EOF
...;field;(:);10000(n,d);(:);field;....
...;field;123.12(b);123(a);123.00(:);....
EOF
  1. 我为每个;做一个换行符
  2. 对于每一行,我至少用(之前的字符和)内的字符串替换字符串。
  3. 然后我将;作为分隔符与xargs和printf合并7行。

答案 1 :(得分:0)

这可能对您有用(GNU sed):

sed -r 's/([0-9]+(\.[0-9]+)?)(\([^)]*\))/\3 \1/g' file

寻找一组数字(可能带有小数点),后跟一对括号,并以所需的方式在每行中全局重新排列它们。

答案 2 :(得分:0)

您的“问题”太长,混乱,并且包含太多无法解决的独立问题,但这是如何从任何sed提供的输入中获得所需的输出:

$ sed 's/\([0-9][0-9.]*\)\(([^)]*)\)/\2 \1/g' file
...;field;(:);(n,d) 10000;(:);field;....
...;field;(b) 123.12;(a) 123;(:) 123.00;....