Bagaimana menemukan kurung yang tidak cocok dalam file teks?

32

Hari ini saya belajar bahwa saya dapat menggunakan perl -c filenameuntuk menemukan kurung keriting yang tak tertandingi {} dalam file sewenang-wenang, tidak harus skrip Perl. Masalahnya adalah, itu tidak berfungsi dengan jenis kurung lain () [] dan mungkin <>. Saya juga melakukan percobaan dengan beberapa plugin Vim yang mengklaim dapat membantu menemukan tanda kurung yang tidak cocok tetapi sejauh ini tidak begitu baik.

Saya memiliki file teks dengan beberapa tanda kurung dan salah satunya hilang! Apakah ada program / script / plugin vim / apa pun yang dapat membantu saya mengidentifikasi braket yang tidak cocok?

phhehehe
sumber

Jawaban:

22

Di Vim, Anda dapat menggunakan [dan ]dengan cepat melakukan perjalanan ke braket yang tak tertandingi terdekat dari jenis yang dimasukkan dalam penekanan tombol berikutnya.

Jadi [{akan membawa Anda kembali ke "{" tak tertandingi terdekat; ])akan membawa Anda ke depan ke ")" yang tak tertandingi terdekat, dan seterusnya.

Shadur
sumber
Hebat, ini sempurna bagi saya. Saya akan menerima jawaban ini, tetapi hanya menunggu untuk melihat apakah ada alat pemrosesan teks yang dapat menganalisis ini.
phunehehe
6
Saya juga akan menambahkan bahwa dalam vim Anda dapat menggunakan% (Shift 5, di AS) untuk segera menemukan braket yang cocok dengan yang Anda gunakan .
atroon
@atroon Ooo, bagus. Aku sendiri belum tahu itu. Saya suka stackexchange kadang-kadang. :)
Shadur
adalah <kbd> [</kbd> dan <kbd>] </kbd> benar-benar melompat ke
wirrbel
Saya menghabiskan hampir satu hari melalui 4000 baris mencoba menemukan yang hilang} di R dan ini adalah jawabannya. Sekali lagi, terima kasih VIM! Tapi saya pikir ini adalah argumen yang bagus untuk memisahkan file kode sumber menjadi potongan yang lebih kecil.
Thomas Browne
7

Pembaruan 2:
Skrip berikut sekarang mencetak nomor baris dan kolom braket yang tidak cocok . Ia memproses satu jenis braket per pemindaian (mis. '[]' '<>' '{}' '()' ...)
Skrip mengidentifikasi braket kanan yang pertama , yang tidak cocok , atau yang pertama dari braket kiri yang tidak dipasangkan ... Pada mendeteksi erroe, ia keluar dengan nomor baris dan kolom

Berikut adalah beberapa contoh keluaran ...


File = /tmp/fred/test/test.in
Pair = ()

*INFO:  Group 1 contains 1 matching pairs

ERROR: *END-OF-FILE* encountered after Bracket 7.
        A Left "(" is un-paired in Group 2.
        Group 2 has 1 un-paired Left "(".
        Group 2 begins at Bracket 3.
  see:  Line, Column (8, 10)
        ----+----1----+----2----+----3----+----4----+----5----+----6----+----7
000008  (   )    (         (         (     )   )                    

Ini skripnya ...


#!/bin/bash

# Itentify the script
bname="$(basename "$0")"
# Make a work dir
wdir="/tmp/$USER/$bname"
[[ ! -d "$wdir" ]] && mkdir -p "$wdir"

# Arg1: The bracket pair 'string'
pair="$1"
# pair='[]' # test
# pair='<>' # test
# pair='{}' # test
# pair='()' # test

# Arg2: The input file to test
ifile="$2"
  # Build a test source file
  ifile="$wdir/$bname.in"
  cp /dev/null "$ifile"
  while IFS= read -r line ;do
    echo "$line" >> "$ifile"
  done <<EOF
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
[   ]    [         [         [
<   >    <         
                   <         >         
                             <    >    >         >
----+----1----+----2----+----3----+----4----+----5----+----6
{   }    {         }         }         }         } 
(   )    (         (         (     )   )                    
ZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZ
EOF

echo "File = $ifile"
# Count how many: Left, Right, and Both
left=${pair:0:1}
rght=${pair:1:1}
echo "Pair = $left$rght"
# Make a stripped-down 'skeleton' of the source file - brackets only
skel="/tmp/$USER/$bname.skel" 
cp /dev/null "$skel"
# Make a String Of Brackets file ... (It is tricky manipulating bash strings with []..
sed 's/[^'${rght}${left}']//g' "$ifile" > "$skel"
< "$skel" tr  -d '\n'  > "$skel.str"
Left=($(<"$skel.str" tr -d "$left" |wc -m -l)); LeftCt=$((${Left[1]}-${Left[0]}))
Rght=($(<"$skel.str" tr -d "$rght" |wc -m -l)); RghtCt=$((${Rght[1]}-${Rght[0]}))
yBkts=($(sed -e "s/\(.\)/ \1 /g" "$skel.str"))
BothCt=$((LeftCt+RghtCt))
eleCtB=${#yBkts[@]}
echo

if (( eleCtB != BothCt )) ; then
  echo "ERROR:  array Item Count ($eleCtB)"
  echo "     should equal BothCt ($BothCt)"
  exit 1
else
  grpIx=0            # Keep track of Groups of nested pairs
  eleIxFir[$grpIx]=0 # Ix of First Bracket in a specific Group
  eleCtL=0           # Count of Left brackets in current Group 
  eleCtR=0           # Count of Right brackets in current Group
  errIx=-1           # Ix of an element in error.
  for (( eleIx=0; eleIx < eleCtB; eleIx++ )) ; do
    if [[ "${yBkts[eleIx]}" == "$left" ]] ; then
      # Left brackets are 'okay' until proven otherwise
      ((eleCtL++)) # increment Left bracket count
    else
      ((eleCtR++)) # increment Right bracket count
      # Right brackets are 'okay' until their count exceeds that of Left brackets
      if (( eleCtR > eleCtL )) ; then
        echo
        echo "ERROR:  MIS-matching Right \"$rght\" in Group $((grpIx+1)) (at Bracket $((eleIx+1)) overall)"
        errType=$rght    
        errIx=$eleIx    
        break
      elif (( eleCtL == eleCtR )) ; then
        echo "*INFO:  Group $((grpIx+1)) contains $eleCtL matching pairs"
        # Reset the element counts, and note the first element Ix for the next group
        eleCtL=0
        eleCtR=0
        ((grpIx++))
        eleIxFir[$grpIx]=$((eleIx+1))
      fi
    fi
  done
  #
  if (( eleCtL > eleCtR )) ; then
    # Left brackets are always potentially valid (until EOF)...
    # so, this 'error' is the last element in array
    echo
    echo "ERROR: *END-OF-FILE* encountered after Bracket $eleCtB."
    echo "        A Left \"$left\" is un-paired in Group $((grpIx+1))."
    errType=$left
    unpairedCt=$((eleCtL-eleCtR))
    errIx=$((${eleIxFir[grpIx]}+unpairedCt-1))
    echo "        Group $((grpIx+1)) has $unpairedCt un-paired Left \"$left\"."
    echo "        Group $((grpIx+1)) begins at Bracket $((eleIxFir[grpIx]+1))."
  fi

  # On error, get Line and Column numbers
  if (( errIx >= 0 )) ; then
    errLNum=0    # Source Line number (current).
    eleCtSoFar=0 # Count of bracket-elements in lines processed so far.
    errItemNum=$((errIx+1)) # error Ix + 1 (ie. "1 based")
    # Read the skeketon file to find the error line-number
    while IFS= read -r skline ; do
      ((errLNum++))
      brackets="${skline//[^"${rght}${left}"]/}" # remove whitespace
      ((eleCtSoFar+=${#brackets}))
      if (( eleCtSoFar >= errItemNum )) ; then
        # We now have the error line-number
        # ..now get the relevant Source Line 
        excerpt=$(< "$ifile" tail -n +$errLNum |head -n 1)
        # Homogenize the brackets (to be all "Left"), for easy counting
        mogX="${excerpt//$rght/$left}"; mogXCt=${#mogX} # How many 'Both' brackets on the error line? 
        if [[ "$errType" == "$left" ]] ; then
          # R-Trunc from the error element [inclusive]
          ((eleTruncCt=eleCtSoFar-errItemNum+1))
          for (( ele=0; ele<eleTruncCt; ele++ )) ; do
            mogX="${mogX%"$left"*}"   # R-Trunc (Lazy)
          done
          errCNum=$((${#mogX}+1))
        else
          # errType=$rght
          mogX="${mogX%"$left"*}"   # R-Trunc (Lazy)
          errCNum=$((${#mogX}+1))
        fi
        echo "  see:  Line, Column ($errLNum, $errCNum)"
        echo "        ----+----1----+----2----+----3----+----4----+----5----+----6----+----7"  
        printf "%06d  $excerpt\n\n" $errLNum
        break
      fi
    done < "$skel"
  else
    echo "*INFO:  OK. All brackets are paired."
  fi
fi
exit
Peter.O
sumber
Script ini brilian!
Jonathan Dumaine
1
Ini luar biasa, tetapi sepertinya selalu mencetak Line, Column (8, 10)tidak peduli file mana yang saya coba. Juga mogXCt=${#mogX}diatur tetapi tidak digunakan di mana pun.
Clayton Dukes
5

Opsi terbaik adalah vim / gvim seperti yang diidentifikasi oleh Shadur, tetapi jika Anda menginginkan skrip, Anda dapat memeriksa jawaban saya untuk pertanyaan serupa di Stack Overflow . Saya ulangi seluruh jawaban saya di sini:

Jika apa yang Anda coba lakukan berlaku untuk bahasa tujuan umum, maka ini adalah masalah non-sepele.

Untuk mulai dengan Anda harus khawatir tentang komentar dan string. Jika Anda ingin memeriksa ini pada bahasa pemrograman yang menggunakan ekspresi reguler, ini akan membuat pencarian Anda lebih sulit lagi.

Jadi sebelum saya bisa masuk dan memberi Anda saran tentang pertanyaan Anda, saya perlu mengetahui batas-batas area masalah Anda. Jika Anda dapat menjamin bahwa tidak ada string, tidak ada komentar dan tidak ada ekspresi reguler yang perlu dikhawatirkan - atau lebih umum lagi dalam kode yang kurung dapat digunakan selain untuk penggunaan yang Anda periksa seimbang - ini akan membuat hidup jauh lebih sederhana.

Mengetahui bahasa yang ingin Anda periksa akan sangat membantu.


Jika saya mengambil hipotesis bahwa tidak ada suara, yaitu bahwa semua tanda kurung adalah tanda kurung yang berguna, strategi saya akan berulang:

Saya hanya akan mencari dan menghapus semua pasangan braket dalam: yang tidak mengandung tanda kurung di dalamnya. Ini paling baik dilakukan dengan menciutkan semua garis menjadi satu garis panjang (dan menemukan mekanisme untuk menambahkan referensi garis, jika Anda perlu mengeluarkan informasi itu). Dalam hal ini pencarian dan penggantiannya cukup sederhana:

Itu membutuhkan sebuah array:

B["("]=")"; B["["]="]"; B["{"]="}"

Dan loop melalui elemen-elemen itu:

for (b in B) {gsub("[" b "][^][(){}]*[" B[b] "]", "", $0)}

File pengujian saya adalah sebagai berikut:

#!/bin/awk

($1 == "PID") {
  fo (i=1; i<NF; i++)
  {
    F[$i] = i
  }
}

($1 + 0) > 0 {
  count("VIRT")
  count("RES")
  count("SHR")
  count("%MEM")
}

END {
  pintf "VIRT=\t%12d\nRES=\t%12d\nSHR=\t%12d\n%%MEM=\t%5.1f%%\n", C["VIRT"], C["RES"], C["SHR"], C["%MEM"]
}

function count(c[)
{
  f=F[c];

  if ($f ~ /m$/)
  {
    $f = ($f+0) * 1024
  }

  C[c]+=($f+0)
}

Skrip lengkap saya (tanpa referensi baris) adalah sebagai berikut:

cat test-file-for-brackets.txt | \
  tr -d '\r\n' | \
  awk \
  '
    BEGIN {
      B["("]=")";
      B["["]="]";
      B["{"]="}"
    }
    {
      m=1;
      while(m>0)
      {
        m=0;
        for (b in B)
        {
          m+=gsub("[" b "][^][(){}]*[" B[b] "]", "", $0)
        }
      };
      print
    }
  '

Output dari skrip tersebut berhenti pada penggunaan kurung terdalam ilegal. Tetapi berhati-hatilah: 1 / skrip ini tidak akan bekerja dengan tanda kurung di komentar, ekspresi reguler atau string, 2 / skrip ini tidak melaporkan di mana dalam file asli masalahnya berada, 3 / meskipun skrip ini akan menghapus semua pasangan seimbang yang berhenti di bagian terdalam kondisi kesalahan dan membuat semua kurung englobbing.

Butir 3 / mungkin merupakan hasil yang dapat dieksploitasi, meskipun saya tidak yakin dengan mekanisme pelaporan yang ada dalam pikiran Anda.

Poin 2 / relatif mudah diterapkan tetapi membutuhkan waktu lebih dari beberapa menit untuk menghasilkan, jadi saya akan menyerahkannya kepada Anda untuk mencari tahu.

Butir 1 / adalah yang rumit karena Anda memasuki ranah baru yang sama sekali sama untuk permulaan dan akhir yang bersarang, atau aturan kutipan khusus untuk karakter khusus ...

pindahkan
sumber
1
Terima kasih, Anda menyelamatkan saya. Memiliki satu penjepit tidak cocok dalam file json baris 30k.
I82Much