【问题标题】:Awk/sed replace newlinesawk/sed 替换换行符
【发布时间】:2016-10-29 18:21:33
【问题描述】:

简介:

我收到了一个 CSV 文件,其中字段分隔符是管道字符(即|)。 该文件具有预定义数量的字段(例如N)。我可以通过读取 CSV 文件的标题来发现 N 的值,我们可以假设它是正确的。

问题:

某些字段错误地包含换行符,这使该行看起来比要求的短(即,它具有M 字段,带有M < N)。

我需要创建一个sh 脚本(不是bash)来修复这些行。

尝试的解决方案:

我尝试创建以下脚本来尝试修复文件:

if [ $# -ne 1 ]
then
    echo "Usage: $0 <filename>"
    exit
fi

# get first line
first_line=$(head -n 1 $1)

# get number of fields
num_separators=$(echo "$first_line" | tr -d -c '|' | awk '{print length}')

cat $1  | awk -v numFields=$(( num_separators + 1 )) -F '|' '
{
    totRecords = NF/numFields
    # loop over lines
    for (record=0; record < totRecords; record++) {
        output = ""
        # loop over fields
        for (i=0; i<numFields; i++) {
            j = (numFields*record)+i+1 
            # replace newline with question mark
            sub("\n", "?", $j)
            output = output (i > 0 ? "|" : "") $j 
        }
        print output
    }
}
'

但是,换行符仍然存在。 我该如何解决这个问题?

CSV 示例:

FIRST_NAME|LAST_NAME|NOTES
John|Smith|This is a field with a
newline
Foo|Bar|Baz

预期输出:

FIRST_NAME|LAST_NAME|NOTES
John|Smith|This is a field with a * newline
Foo|Bar|Baz

* I don't care about the replacement, it could be a space, a question mark, whatever except a newline or a pipe (which would create a new field)

【问题讨论】:

  • 您能否包含示例输入和预期输出?
  • 是否可以假设如果一个字段包含换行符,那么该记录将包含更少的字段然后标题以及下一条记录将包含更少的字段?
  • @anubhava 是的,我会尽快包含它
  • W.r.t.示例输入,没有更多信息,我认为无论如何都不知道额外的换行符是在第一条记录的最后一个字段中,还是在第二条记录的第一个字段中。当然,作为人类,我们可以看到您的意思是它位于第一条记录的最后一个字段中,但是程序怎么知道呢?
  • @jas 很好的观察,我忘了提。据我所知(我不是此类文件的制作者),第一个字段不应该由人输入(它有其他输入字段并且可以错误地插入换行符),但它是一种ID,所以我们可以安全地假设第一个字段不包含任何奇怪的字符。在这种情况下,错误必须存在于最后一个字段中。

标签: shell csv awk replace


【解决方案1】:
$ cat tst.awk
BEGIN { FS=OFS="|" }
NR==1 { reqdNF = NF; printf "%s", $0; next }
{ printf "%s%s", (NF < reqdNF ? " " : ORS), $0 }
END { print "" }

$ awk -f tst.awk file.csv
FIRST_NAME|LAST_NAME|NOTES
John|Smith|This is a field with a newline
Foo|Bar|Baz

如果这不是您想要的,请编辑您的问题以提供更具代表性的示例输入和相关输出。

【讨论】:

  • 这好多了+1
  • 很好的答案,我什至不需要“初始化”脚本。我不知道awk,但不得不承认它真的很强大。
  • 是的。如果您可能需要进行任何文本操作并且可以使用 CLI,我强烈推荐 Arnold Robbins 的《Effective Awk Programming, 4th Edition》一书。
  • 这很漂亮。我使用 awk 进行简单的现场切割,但这太优雅了。刚买了@EdMorton推荐的文字。
【解决方案2】:

基于最后一个字段可能包含一个换行符的假设。使用 tacsed

tac file.csv | sed -n '/|/!{h;n;x;H;x;s/\n/ * /p;b};p' | tac 

输出:

FIRST_NAME|LAST_NAME|NOTES
John|Smith|This is a field with a * newline
Foo|Bar|Baz

它是如何工作的。向后读取文件,sed 更容易没有前向引用。如果一行没有'|'分隔符/|/!,运行花括号{}; 中的代码块,否则只需p 打印该行。代码块:

  1. h; 将无分隔符的行存储在 sedhold 缓冲区中。
  2. n; 获取另一行,因为我们正在向后阅读,这是应该附加到的行。
  3. x; 交换保持缓冲区和模式缓冲区。
  4. H; 追加模式缓冲区保持缓冲区。
  5. x; 将新添加的行交换到模式缓冲区,现在一个缓冲区中有两行。
  6. s/\n/ * /p; 将中间换行替换为“ * ”,现在只有一个 longer 行;并打印。
  7. b重新开始,离开代码块。

tac重新反转文件;完成。

【讨论】:

  • 您好 agc,感谢您的回答。您的代码所基于的假设是额外的换行符在最后一个字段中,对吧?
  • 好的,谢谢。我只是想在使用它之前澄清这一点:) 但是很好的解释!
猜你喜欢
  • 2011-01-23
  • 2020-10-29
  • 2017-02-08
  • 2013-05-30
  • 2013-11-03
  • 2017-09-06
  • 1970-01-01
  • 2022-01-03
  • 1970-01-01
相关资源
最近更新 更多