簡體   English   中英

如何在幾秒鍾內創建大型CSV?

[英]How do I create large CSVs in seconds?

我正在嘗試快速創建數千個大型CSV。 此函數生成CSV:

function csvGenerator () {

  for ((i=1; i<=$NUMCSVS; i++)); do
    CSVNAME=$DIRNAME"-"$CSVPREFIX$i$CSVEXT
    HEADERARRAY=()

    if [[ ! -e $CSVNAME ]]; then #Only create csv file if it not exist
      touch $CSVNAME
      echo "file: "$CSVNAME "created at $(date)" >> ../status.txt
    fi

    for ((j=1; j<=$NUMCOLS; j++)); do

      if  (( j < $NUMCOLS )) ; then
        HEADERNAME=$DIRNAME"-csv-"$i"-header-"$j", "
      elif (( j == $NUMCOLS )) ; then
        HEADERNAME=$DIRNAME"-csv-"$i"-header-"$j
      fi
      HEADERARRAY+=$HEADERNAME

    done

    echo $HEADERARRAY > $CSVNAME

    for ((k=1; k<=$NUMROWS; k++)); do
      ROWARRAY=()

      for ((l=1; l<=$NUMCOLS; l++)); do

        if (( l < $NUMCOLS )) ; then
          ROWVALUE=$DIRNAME"-csv-"$i"-r"$k"c"$l", "
        elif (( l == $NUMCOLS )) ; then
          ROWVALUE=$DIRNAME"-csv-"$i"-r"$k"c"$l
        fi
        ROWARRAY+=$ROWVALUE

      done

      echo $ROWARRAY >> $CSVNAME

    done

  done

}

該腳本大約需要3分鍾才能生成具有10萬行和70列的CSV。 我需要怎么做才能以1 CSV /〜10秒的速度生成這些CSV?

首先,我要說bash和“表演者”通常不會在同一句話中在一起。 正如其他評論員所建議的那樣, awk在某些方面可能是一個很好的選擇。

我還沒有機會運行您的代碼,但是它每行打開和關閉輸出文件一次-在本示例中為100,000次。 每次必須搜索到文件的末尾,以便它可以追加最新的行。

嘗試將實際的生成( for ((j=1; j<=$NUMCOLS; j++)); do )拉入一個新函數,如generateCsvContents 在該新函數中,不要引用$CSVNAME$CSVNAME刪除echo語句上的重定向。 然后,在原始函數中,調用新函數並將其輸出重定向到文件名。 大致:

function csvGenerator () {
 for ((i=1; i<=NUMCSVS; i++)); do
    CSVNAME=$DIRNAME"-"$CSVPREFIX$i$CSVEXT

    if [[ ! -e $CSVNAME ]]; then #Only create csv file if it not exist
      echo "file: $CSVNAME created at $(date)" >> ../status.txt
    fi

    # This will create $CSVNAME if it doesn't yet exist
    generateCsvContents > "$CSVNAME"
  done
}

function generateCsvContents() {
  HEADERARRAY=()
  for ((j=1; j<=NUMCOLS; j++)); do
    if  (( j < NUMCOLS )) ; then
      HEADERNAME=$DIRNAME"-csv-"$i"-header-"$j", "
    elif (( j == NUMCOLS )) ; then
      HEADERNAME=$DIRNAME"-csv-"$i"-header-"$j
    fi
    HEADERARRAY+=$HEADERNAME
  done

  echo $HEADERARRAY

  for ((k=1; k<=NUMROWS; k++)); do
    ROWARRAY=()
    for ((l=1; l<=NUMCOLS; l++)); do
      if (( l < NUMCOLS )) ; then
        ROWVALUE=$DIRNAME"-csv-"$i"-r"$k"c"$l", "
      elif (( l == NUMCOLS )) ; then
        ROWVALUE=$DIRNAME"-csv-"$i"-r"$k"c"$l
      fi
      ROWARRAY+=$ROWVALUE
    done
    echo "$ROWARRAY"
  done
}

我認為答案是“不是這樣”。

這里有一些問題。

  • 您沒有將數組用作數組。 當您將它們視為字符串時,只會影響數組中的第一個元素,這會產生誤導。
  • 您使用>>的方式會使輸出文件每行打開和關閉一次。 這可能是浪費的。
  • 您沒有引用變量。 實際上,您是在引用不需要的內容,而不是在引用那些內容。
  • 不建議使用大寫的變量名,因為有可能與系統變量發生沖突。 參考
  • Bash對此並不擅長。 真。

函數的清理版本可能如下所示:

csvGenerator2() {

  for (( i=1; i<=NUMCSVS; i++ )); do
    CSVNAME="$DIRNAME-$CSVPREFIX$i$CSVEXT"

    # Only create csv file if it not exist
    [[ -e "$CSVNAME" ]] && continue

    touch "$CSVNAME"
    date "+[%F %T] created: $CSVNAME" | tee -a status.txt >&2

    HEADER=""
    for (( j=1; j<=NUMCOLS; j++ )); do
      printf -v HEADER '%s, %s-csv-%s-header-%s' "$HEADER" "$DIRNAME" "$i" "$j"
    done

    echo "${HEADER#, }" > "$CSVNAME"

    for (( k=1; k<=NUMROWS; k++ )); do

      ROW=""
      for (( l=1; l<=NUMCOLS; l++ )); do
        printf -v ROW '%s, %s-csv-%s-r%sc%s' "$ROW" "$DIRNAME" "$i" "$k" "$l"
      done

      echo "${ROW#, }"

    done >> "$CSVNAME"

  done

}

(請注意,由於我很懶,我沒有將變量切換為小寫,但這仍然是一個好主意。)

如果要在awk中進行功能上等效的操作:

csvGenerator3() {
  awk -v NUMCSVS="$NUMCSVS" -v NUMCOLS="$NUMCOLS" -v NUMROWS="$NUMROWS" -v DIRNAME="$DIRNAME" -v CSVPREFIX="$CSVPREFIX" -v CSVEXT="$CSVEXT" '
    BEGIN {
      for ( i=1; i<=NUMCSVS; i++) {
        out=sprintf("%s-%s%s%s", DIRNAME, CSVPREFIX, i, CSVEXT)
        if (!system("test -e " CSVNAME)) continue
        system("date '\''+[%F %T] created: " out "'\'' | tee -a status.txt >&2")

        comma=""
        for ( j=1; j<=NUMCOLS; j++ ) {
          printf "%s%s-csv-%s-header-%s", comma, DIRNAME, i, j > out
          comma=", "
        }
        printf "\n" >> out

        for ( k=1; k<=NUMROWS; k++ ) {
          comma=""
          for ( l=1; l<=NUMCOLS; l++ ) {
            printf "%s%s-csv-%s-r%sc%s", comma, DIRNAME, i, k, l >> out
            comma=", "
          }
          printf "\n" >> out
        }
      }
    }
  '
}

請注意,awk不會受到前面提到的bash的打開/關閉開銷的影響; 當文件用於輸出或作為管道時,該文件將被打開一次並保持打開狀態直到關閉。

比較兩者確實突出了您需要做出的選擇:

$ time bash -c '. file; NUMCSVS=1 NUMCOLS=10 NUMROWS=100000 DIRNAME=2 CSVPREFIX=x CSVEXT=.csv csvGenerator2'
[2019-03-29 23:57:26] created: 2-x1.csv

real    0m30.260s
user    0m28.012s
sys     0m1.395s
$ time bash -c '. file; NUMCSVS=1 NUMCOLS=10 NUMROWS=100000 DIRNAME=3 CSVPREFIX=x CSVEXT=.csv csvGenerator3'
[2019-03-29 23:58:23] created: 3-x1.csv

real    0m4.994s
user    0m3.297s
sys     0m1.639s

請注意,即使是我優化的bash版本也僅比原始代碼快一點。

將您的兩個內部for循環重構為這樣的循環將節省時間:

for ((j=1; j<$NUMCOLS; ++j)); do
  HEADERARRAY+=$DIRNAME"-csv-"$i"-header-"$j", "
done
HEADERARRAY+=$DIRNAME"-csv-"$i"-header-"$NUMCOLS

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM