【问题标题】:bash recursive CSV parser [closed]bash递归CSV解析器[关闭]
【发布时间】:2012-05-13 03:55:12
【问题描述】:

我已将此问题移至 code review

我在 bash 中编写了一个递归下降解析器。

我想知道你能否指出一些对我有帮助的东西。它支持反斜杠转义和带解析错误报告的引用字段。

脚本在某些方面类似于cut.. 从文件中获取输入或stdin 允许用户选择他们想要打印的行和字段。分别使用选项-l-f。可以打印字段列表,并且可以使用选项 --list '1 2 3 4 5' 和 --list-seperator $'\n' 为该列表指定自定义分隔符。

#!/bin/bash

shopt -s extglob; # we need maxiumum expression capabilities enabled

# option variables
declare list='' delim=$'\n' field='' lineNum=1;

while [[ ${1:0:1} = '-' ]]; do # Parse user arguments

    case $1 in
        -f)
            field=$2; shift 2;
        ;;
        -l)
            lineNum=$2; shift 2;
        ;;
        --list-seperator)
            delim="$2"; shift 2;
        ;;
        --list)
            list="$2"; shift 2;
        ;;
        *) break;;
    esac

done

# open a user supplied file on stdin
[[ -e "$1" ]] && exec 0<$1;

# data from 'read/getline'
declare input='';

# we are using sed to optimize input, the command just prints the desired line
read -r input < <(sed -n ${lineNum}p) 
# why doesn't the above work as a pipe into read?

# range of this line
declare strLen=${#input} value='';

# data processing variables
declare symbol='' value='';

# REGEX symbol "classes"
declare nothing='' comma='[,]' quote='["]' backslash='[\]' text='[^,\"]';

# integers:
declare -i iPos=-1 tPos=0;

# output array:
declare -a items=();

NextSymbol() {

    symbol="${input:$((++iPos)):1}"; # get next char from string

    (( iPos < strLen )); # return false if we are out of range

}

Accept() {

    [[ -z "$symbol" && -z "$1" ]] && return 0; # accept "nothing/empty"

    # if you can meld the above line into the next line
    # let me know: pc.wiz.tt@gmail.com; this is some kind of bug!
    # becare careful because expect expects 'nothing' to be empty.
    # that's why it says 'end of input'

    [[ "$symbol" =~ ^$1$ ]] && NextSymbol && return 0; # match symbol
}

Expect() {
    Accept "$1" && return
    local msg="$0: parse failure: line $lineNum: expected symbol: "
    echo "$msg'${1:-end of input}'" >&2;
    echo "$0: found: '$symbol'" >&2;
    exit 1;
}

value() {

    while Accept $text; do # symbol will not be what we expect here
        value+=${input:$((iPos-1)):1}; # so get what we expect
    done

    Accept $nothing && { # this will only happen at end of the string
        value+=${input:$((iPos-1)):1} # get the last char
        pushValue; # push the data into the array
    }

}

pushValue() {
    items[tPos++]=$value;
    value=''; # clear value!
}

quote() {

    until [[ $symbol =~ $quote || -z $symbol ]]; do
        value+=$symbol;
        NextSymbol;
    done

    Expect $quote;

}

line() {

    Accept $quote && {
        quote
        line;
    }

    Accept $backslash && {
        value+=$symbol;
        NextSymbol;
        value;
        line;
    }

    Accept $comma && {
        pushValue;
        line;
    }

    Accept $text && {
        value=${input:$((iPos-1)):1};
        value;
        line;
    }

}

NextSymbol;
    line;
        Expect $nothing


[[ $field ]] && { # want field    
    echo "${items[field-1]}" # print the item
                             # (index justified for human use)
    exit;
}

[[ $list ]] && { # want list
    for index in $list; do
        echo -n "${items[index-1]}${delim}" # print the list 
                                            # (index justified for human use)
    done
    exit;
}

exit 1; # no field or list

【问题讨论】:

  • @MarcB 不删除,我们可以直接关闭它,让我留下codereview的链接吗?
  • 我在侧边栏看到很多关于脚本和 CSV 的问题。即使它是“离题”,这也会很有帮助。
  • 只有 43ms 慢。在一个英特尔原子上。在大约 100K 左右的小文件中,用户不会注意到任何内容。
  • 请不要交叉发布问题;这样做会给我们在两个网站上带来不必要的麻烦。如果问题不在此处,但与代码审查主题有关,请将其标记为迁移。由于您已将其转发到那里,因此我正在当场关闭它,但我可能不会删除它。

标签: bash csv awk recursive-descent


【解决方案1】:

说真的,让它更快的方法是用一种非解释性的语言编写。

shell 可能不是解析器的最佳选择,原因与我不会用 6502 汇编语言编写会计包或用 COBOL 编写操作系统的原因相同。

而且,老实说,awk 不会变得更好。它是顺序文本处理的理想选择,但将其应用于像解析器这样复杂的东西会很费力。

有时,您只需要重新考虑您正在使用的工具。如果你做不到它是像 C 这样的编译语言,至少要考虑一下像 Python 这样的面向字节码的语言。

【讨论】:

  • 当然可以,但我并没有尝试解析 USGS terra 服务器数据库.. 只是一些小的 bash 脚本。编译后我可以在 c 或 asm 中完成,实际上我已经完成了。这是一个脚本项目。不幸的是,所以 awk 或 bash。
  • @Triston,我的建议仍然有效。如果你想要速度,解释语言不是最好的方法。或者,换个角度来看,如果你想要一个好的 shell 项目,递归下降解析器可能不是一个完美的展示。
  • 我也有一个 AWK 编译器。但就像我说的,它只是一个脚本项目。
  • 它仍然是个好建议,我完全同意。但是二进制不在脚本领域。这就是我被限制的域。
  • @Triston,43 毫秒?以毫秒为单位?我不完全确定我会认为这很慢:-)
猜你喜欢
  • 2012-05-21
  • 1970-01-01
  • 1970-01-01
  • 2015-03-08
  • 1970-01-01
  • 1970-01-01
  • 2022-09-17
  • 2012-08-11
  • 2014-04-14
相关资源
最近更新 更多