วิธีค้นหาวงเล็บเหลี่ยมที่ไม่ตรงกันในไฟล์ข้อความ?


32

วันนี้ฉันได้เรียนรู้ว่าฉันสามารถใช้perl -c filenameเพื่อค้นหาวงเล็บปีกกาที่ไม่มีใครเทียบ {} ในไฟล์โดยพลการไม่จำเป็นต้องเป็นสคริปต์ Perl ปัญหาคือมันไม่ทำงานกับวงเล็บประเภทอื่น () [] และอาจ <> ฉันยังได้ทดลองกับปลั๊กอิน Vim หลายตัวที่อ้างว่าช่วยค้นหาวงเล็บเหลี่ยมที่ไม่ตรงกัน แต่ก็ยังไม่ค่อยดีนัก

ฉันมีไฟล์ข้อความที่มีวงเล็บค่อนข้างน้อยและหนึ่งในนั้นหายไป! มีโปรแกรม / สคริปต์ / vim plugin / สิ่งใดบ้างที่สามารถช่วยฉันระบุวงเล็บเหลี่ยมที่ไม่มีใครเทียบได้?

คำตอบ:


22

ในกลุ่มคุณสามารถใช้[และ]เดินทางไปยังวงเล็บเหลี่ยมที่ไม่ตรงกันที่ใกล้ที่สุดของประเภทที่ป้อนในการกดแป้นถัดไป

ดังนั้น[{จะพาคุณกลับไปยังตำแหน่งที่ใกล้เคียงที่สุด "{"; ])จะนำคุณไปข้างหน้ากับ ")" ที่ไม่มีใครเทียบได้และอื่น ๆ ที่ใกล้ที่สุด


เยี่ยมมากมันเหมาะสำหรับฉัน ฉันจะยอมรับคำตอบนี้ แต่รอเพื่อดูว่ามีเครื่องมือการประมวลผลข้อความที่สามารถวิเคราะห์ได้หรือไม่
phunehehe

6
ฉันจะเพิ่มเข้าไปในเสียงเรียกเข้าคุณสามารถใช้% (Shift 5 ในสหรัฐอเมริกา) เพื่อค้นหาวงเล็บเหลี่ยมที่ตรงกันสำหรับอันที่คุณอยู่
atroon

@atroon Ooo ดี ไม่รู้ว่าตัวเองยัง บางครั้งฉันก็รัก stackexchange :)
Shadur

คือ <kbd> [</kbd> และ <kbd>] </kbd> จริงๆกระโดดไปที่
wirrbel

ฉันใช้เวลาเกือบหนึ่งวันในการผ่าน 4000 บรรทัดเพื่อค้นหาสิ่งที่หายไป} ใน R และนี่คือคำตอบ ขอขอบคุณ VIM อีกครั้ง! แต่ฉันคิดว่านี่เป็นอาร์กิวเมนต์ที่ดีสำหรับการแยกไฟล์ซอร์สโค้ดเป็นชิ้นเล็ก ๆ
โทมัสบราวน์

7

การปรับปรุงที่ 2:
สคริปต์ต่อไปนี้ในขณะนี้พิมพ์ออกหมายเลขบรรทัดและคอลัมน์ของ mismached วงเล็บ มันจะประมวลผลชนิดหนึ่งวงเล็บต่อการสแกน (ie. '[]' '<>' '{}' '() ... )
สคริปต์ระบุแรก , วงเล็บขวาตรงกันหรือครั้งแรกของใด ๆวงเล็บซ้ายยกเลิกการจับคู่ ... ในการตรวจจับเอโรโรมันจะออกมาพร้อมกับหมายเลขบรรทัดและคอลัมน์

นี่คือตัวอย่างผลลัพธ์ ...


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

นี่คือสคริปต์ ...


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

สคริปต์นี้ยอดเยี่ยม!
Jonathan Dumaine

1
มันยอดเยี่ยม แต่ดูเหมือนว่าจะพิมพ์เสมอLine, Column (8, 10)ไม่ว่าฉันจะลองไฟล์ไหน นอกจากนี้ยังmogXCt=${#mogX}มีการตั้งค่า แต่ไม่ได้ใช้ทุกที่
Clayton Dukes

5

ตัวเลือกที่ดีที่สุดคือเป็นกลุ่ม / gvim ที่ระบุไว้โดย Shadur แต่ถ้าคุณต้องการสคริปต์คุณสามารถตรวจสอบคำตอบของฉันจะเป็นคำถามที่คล้ายกันในกองมากเกิน ฉันทำซ้ำคำตอบทั้งหมดของฉันที่นี่:

หากสิ่งที่คุณพยายามจะนำไปใช้กับภาษาที่ใช้โดยทั่วไปแล้วนี่เป็นปัญหาที่ไม่สำคัญ

เริ่มต้นด้วยคุณจะต้องกังวลเกี่ยวกับความคิดเห็นและสตริง หากคุณต้องการตรวจสอบสิ่งนี้ในภาษาการเขียนโปรแกรมที่ใช้นิพจน์ทั่วไปสิ่งนี้จะทำให้ภารกิจของคุณหนักขึ้นอีกครั้ง

ดังนั้นก่อนที่ฉันจะเข้ามาและให้คำแนะนำกับคำถามของคุณฉันจำเป็นต้องรู้ขอบเขตของปัญหาของคุณ หากคุณสามารถรับประกันได้ว่าไม่มีสตริงไม่มีความคิดเห็นและไม่มีการแสดงออกปกติที่ต้องกังวล - หรือไม่มีรหัสทั่วไปที่วงเล็บสามารถนำไปใช้นอกเหนือจากการใช้งานที่คุณกำลังตรวจสอบว่ามีความสมดุล - สิ่งนี้จะ ทำให้ชีวิตง่ายขึ้นมาก

การรู้ภาษาที่คุณต้องการตรวจสอบจะเป็นประโยชน์


หากฉันใช้สมมุติฐานว่าไม่มีเสียงดังกล่าวนั่นคือทุกวงเล็บเป็นวงเล็บเหลี่ยมที่มีประโยชน์กลยุทธ์ของฉันจะเป็นแบบวนซ้ำ:

ฉันเพียงแค่มองหาและลบคู่วงเล็บภายในทั้งหมด: สิ่งเหล่านั้นที่ไม่มีวงเล็บอยู่ภายใน สิ่งนี้ทำได้ดีที่สุดโดยการยุบบรรทัดทั้งหมดเป็นบรรทัดยาวเดี่ยว (และค้นหากลไกเพื่อเพิ่มการอ้างอิงบรรทัดหากคุณต้องการข้อมูลนั้นออกมา) ในกรณีนี้การค้นหาและแทนที่ง่าย ๆ :

มันต้องมีอาร์เรย์:

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

และวนรอบองค์ประกอบเหล่านั้น:

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

ไฟล์ทดสอบของฉันเป็นดังนี้:

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

สคริปต์แบบเต็มของฉัน (ไม่มีการอ้างอิงบรรทัด) เป็นดังนี้:

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

ผลลัพธ์ของสคริปต์นั้นจะหยุดลงที่การใช้งานวงเล็บเหลี่ยมสุดผิดกฎหมาย แต่ระวัง: 1 / สคริปต์นี้จะไม่ทำงานกับเครื่องหมายวงเล็บในความคิดเห็นการแสดงออกปกติหรือสตริง 2 / มันไม่ได้รายงานว่าปัญหาอยู่ที่ไหนในไฟล์ต้นฉบับ 3 / แม้ว่ามันจะลบคู่ที่สมดุลทั้งหมดที่จะหยุดที่ด้านในสุด เงื่อนไขข้อผิดพลาดและช่วยให้วงเล็บทั้งหมด englobbing

จุดที่ 3 / อาจเป็นผลลัพธ์ที่เอาเปรียบแม้ว่าฉันจะไม่แน่ใจเกี่ยวกับกลไกการรายงานที่คุณมีอยู่ในใจ

จุดที่ 2 / ใช้งานง่าย แต่ใช้เวลามากกว่าสองสามนาทีในการผลิตดังนั้นฉันจะปล่อยให้คุณคิดออก

จุดที่ 1 / เป็นสิ่งที่ยุ่งยากเนื่องจากคุณเข้าสู่อาณาจักรใหม่ของการแข่งขันบางครั้งการเริ่มต้นและการสิ้นสุดที่ซ้อนกันหรือกฎการอ้างอิงพิเศษสำหรับอักขระพิเศษ ...


1
ขอบคุณคุณช่วยฉัน มีวงเล็บปีกกาที่ไม่ตรงกันหนึ่งไฟล์ในไฟล์ json 30k line
I82
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.