Bir metin dosyasında eşleşmeyen parantez nasıl bulunur?


32

Bugün perl -c filename, mutlaka Perl betiklerinde değil, rastgele dosyalarda rastgele küme parantezlerini bulmak için kullanabileceğimi öğrendim . Sorun şu ki, diğer parantez türleriyle () [] ve belki de <> çalışmaz. Eşsiz parantezleri bulmaya yardımcı olduğunu iddia eden ama şu ana kadar pek iyi olmayan çeşitli Vim eklentileri ile de deneyler yaptım.

Epeyce parantez içeren bir metin dosyasına sahibim ve bunlardan biri eksik! Eşsiz braketi tanımlamama yardımcı olabilecek herhangi bir program / script / vim eklentisi / var mı?

Yanıtlar:


22

Vim'de bir sonraki tuşa girilen türün en yakın eşleştirilmemiş braketine hızlıca seyahat etmek için kullanabilir [ve kullanabilirsiniz ].

Öyleyse [{sizi en yakın eşleşmeyecek "{"; ])sizi en yakın eşleşmemiş ")" ve benzerlerine götürürdü.


Harika, bu benim için mükemmel. Bu cevabı kabul etmek üzereyim, ancak yalnızca bunu analiz edebilecek bir metin işleme aracı olup olmadığını görmek için bekliyorum.
Phunehehe

6
Ayrıca vim'de, bulunduğunuzla eşleşen eşleme dirseğini hemen bulmak için% (Shift 5, ABD'de) seçeneğini kullanabileceğinizi de ekleyeceğim .
atroon

@atroon Ooo, güzel. Bunu henüz kendim bilmiyordum. Bazen yığın değişimini seviyorum. :)
Shadur

<kbd> [</kbd> ve <kbd>] </kbd>, gerçekten
wirrbel

Neredeyse bir gününü 4000 hatlarını geçip R'yi bulmaya çalışırken harcadım} ve cevap buydu. Yine, teşekkür VIM! Ancak bunun kaynak kod dosyalarını daha küçük parçalara bölmek için iyi bir argüman olduğunu düşünüyorum.
Thomas Browne,

7

Güncelleme 2:
Aşağıdaki komut dosyası şimdi yanlış bir braketin satır numarasını ve sütununu yazdırıyor . Bu tarama başına bir braket türünü işleyen (yani. '[]' '<>' '{}' '()' ...)
komut tanımlar birinci , eşsiz sağ ayraç veya herhangi ilk un-eşleştirilmiş sol destekten ... Bir erroe tespitinde, satır ve sütun numaralarıyla çıkar.

İşte bazı örnek çıktılar ...


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  (   )    (         (         (     )   )                    

İşte senaryo ...


#!/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

Bu senaryo mükemmel!
Jonathan Dumaine,

1
Bu harika, ancak Line, Column (8, 10)denemem gereken dosya ne olursa olsun her zaman yazdırıyor gibi görünüyor . Ayrıca mogXCt=${#mogX}ayarlanır ancak hiçbir yerde kullanılmaz.
Clayton Dukes,

5

En iyi seçenek Shadur tarafından tanımlandığı şekilde vim / gvim'dir, ancak bir komut dosyası istiyorsanız , Stack Overflow'taki benzer bir soruya cevabımı kontrol edebilirsiniz . Burada tüm cevabımı tekrarlıyorum:

Yapmaya çalıştığınız şey genel amaçlı bir dil için geçerliyse, bu önemsiz bir sorundur.

Başlamak için yorum ve dizeler konusunda endişelenmeniz gerekecek. Bunu, düzenli ifadeler kullanan bir programlama dilinde kontrol etmek istiyorsanız, bu, arayışınızı yeniden zorlaştıracaktır.

Bu yüzden içeri girip size sorunuzla ilgili herhangi bir tavsiyede bulunmadan önce sorunlu bölgenizin sınırlarını bilmem gerekiyor. Herhangi bir karakter dizisi, yorum yok ve endişelenecek düzenli ifadeler olmadığını - ya da kodun genel olarak hiçbir yerde, parantezlerin dengeli olup olmadığını kontrol etmekte kullandıklarınız dışında kullanılabileceğini garanti edemezsiniz - bu hayatı çok daha basit hale getirir.

Kontrol etmek istediğiniz dili bilmek faydalı olacaktır.


Gürültünün olmadığı, yani tüm parantezlerin yararlı parantezler olduğu hipotezini alırsam stratejim yinelemeli olur:

İçteki parantez çiftlerini arar ve çıkarırdım: içinde parantez içermeyenleri. Bu, en iyi şekilde tüm satırları tek bir uzun satıra daraltmak suretiyle yapılır (ve satır referansları eklemek için bir mekanizma bulabilirsiniz, bu bilgiyi çıkarmanız gerekir). Bu durumda, arama ve değiştirme oldukça basittir:

Bir dizi gerektirir:

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

Ve bu elementlerin arasında bir döngü:

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

Test dosyam aşağıdaki gibidir:

#!/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)
}

Tam betiğim (satır referansı olmadan) aşağıdaki gibidir:

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
    }
  '

Bu betiğin çıktısı, parantezlerin en yasa dışı kullanımında durur. Ancak dikkat: 1 / bu betiğin yorumlarda, düzenli ifadelerde veya dizelerde parantez içinde çalışmadığı, 2 / sorunun orjinal dosyada nerede bulunduğunu bildirmediği, 3 / en başta durduğu tüm dengeli çiftleri kaldıracak olmasına rağmen hata koşulları ve bütün tutma dirseklerini tutar.

Nokta 3 / muhtemelen sömürülebilir bir sonuçtur, ancak aklınızdaki raporlama mekanizmasından emin değilim.

Nokta 2 / uygulanması nispeten kolaydır ancak üretilmesi birkaç dakikadan fazla zaman alır, bu yüzden bunu çözmeniz için size bırakacağım.

Nokta 1 / yanıltıcıdır çünkü yepyeni iç içe başlangıçlar ve bitişler veya özel karakterler için özel alıntı kuralları ...


1
Sağol, beni kurtardın. Bir 30k satır json dosyasında bir uyumsuz ayraç vardı.
I82Mart
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.