Làm thế nào để tìm dấu ngoặc chưa từng có trong một tệp văn bản?


32

Hôm nay tôi đã học được rằng tôi có thể sử dụng perl -c filenameđể tìm các dấu ngoặc nhọn chưa từng có {} trong các tệp tùy ý, không nhất thiết phải là các tập lệnh Perl. Vấn đề là, nó không hoạt động với các loại dấu ngoặc khác () [] và có thể <>. Tôi cũng đã có những thử nghiệm với một số plugin Vim tuyên bố sẽ giúp tìm các dấu ngoặc chưa từng có nhưng cho đến nay không được tốt lắm.

Tôi có một tệp văn bản với khá nhiều dấu ngoặc và một trong số chúng bị thiếu! Có bất kỳ plugin chương trình / script / vim / bất cứ điều gì có thể giúp tôi xác định khung chưa từng có?

Câu trả lời:


22

Trong Vim, bạn có thể sử dụng []nhanh chóng di chuyển đến khung chưa từng có gần nhất của loại được nhập trong tổ hợp phím tiếp theo.

Vì vậy, [{sẽ đưa bạn trở lại với "{" chưa từng có gần nhất; ])sẽ đưa bạn về phía trước để "chưa từng có" gần nhất, v.v.


Tuyệt vời, điều này là hoàn hảo cho tôi. Tôi sắp chấp nhận câu trả lời này, nhưng chỉ chờ xem liệu có công cụ xử lý văn bản nào có thể phân tích điều này không.
phunehehe

6
Tôi cũng sẽ thêm rằng trong vim, bạn có thể sử dụng% (Shift 5, ở Hoa Kỳ) để tìm ngay khung phù hợp cho cái bạn đang bật.
atroon

@atroon Ooo, tốt đẹp. Tôi không biết rằng một trong những bản thân mình. Đôi khi tôi thích stackexchange. :)
Shadur

là <kbd> [</ kbd> và <kbd>] </ kbd> thực sự nhảy đến
wirrbel

Tôi đã dành gần một ngày để trải qua 4000 dòng cố gắng tìm kiếm sự mất tích} trong R và đây là câu trả lời. Một lần nữa, cảm ơn bạn VIM! Nhưng tôi nghĩ rằng đây là một đối số tốt để chia các tệp mã nguồn thành các phần nhỏ hơn.
Thomas Browne

7

Cập nhật 2:
Tập lệnh sau bây giờ in ra số dòng và cột của dấu ngoặc không khớp . Nó xử lý một loại khung cho mỗi lần quét (nghĩa là '[]' '<>' '{}' '()' ...)
Tập lệnh xác định dấu ngoặc phải đầu tiên , không khớp hoặc đầu tiên của bất kỳ dấu ngoặc trái nào không được ghép ... Khi phát hiện một erroe, nó thoát với số dòng và số cột

Đây là một số đầu ra mẫu ...


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

Đây là kịch bản ...


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

Kịch bản này là tuyệt vời!
Jonathan Dumaine

1
Điều này thật tuyệt vời, nhưng dường như nó luôn in Line, Column (8, 10)bất kể tập tin nào tôi thử. Cũng mogXCt=${#mogX}được thiết lập nhưng không được sử dụng ở bất cứ đâu.
Clayton Dukes

5

Tùy chọn tốt nhất là vim / gvim như được xác định bởi Shadur, nhưng nếu bạn muốn có một tập lệnh, bạn có thể kiểm tra câu trả lời của tôi cho một câu hỏi tương tự trên Stack Overflow . Tôi nhắc lại toàn bộ câu trả lời của tôi ở đây:

Nếu những gì bạn đang cố gắng áp dụng cho một ngôn ngữ có mục đích chung, thì đây là một vấn đề không hề nhỏ.

Để bắt đầu với bạn sẽ phải lo lắng về ý kiến ​​và chuỗi. Nếu bạn muốn kiểm tra điều này trên một ngôn ngữ lập trình sử dụng các biểu thức thông thường, điều này sẽ khiến nhiệm vụ của bạn trở lại khó khăn hơn.

Vì vậy, trước khi tôi có thể đến và cho bạn bất kỳ lời khuyên nào về câu hỏi của bạn, tôi cần biết giới hạn của khu vực vấn đề của bạn. Nếu bạn có thể đảm bảo rằng không có chuỗi, không có nhận xét và không có biểu thức chính quy nào phải lo lắng - hoặc nói chung là không có mã nào có thể được sử dụng ngoài các cách sử dụng mà bạn đang kiểm tra xem chúng có cân bằng không - điều này sẽ làm cho cuộc sống đơn giản hơn rất nhiều

Biết ngôn ngữ mà bạn muốn kiểm tra sẽ hữu ích.


Nếu tôi đưa ra giả thuyết rằng không có tiếng ồn, tức là tất cả các dấu ngoặc là dấu ngoặc hữu ích, chiến lược của tôi sẽ được lặp lại:

Tôi chỉ đơn giản là tìm kiếm và loại bỏ tất cả các cặp ngoặc trong: những cặp không chứa dấu ngoặc bên trong. Điều này được thực hiện tốt nhất bằng cách thu gọn tất cả các dòng thành một dòng dài duy nhất (và tìm một cơ chế để thêm các tham chiếu dòng, nếu bạn cần lấy thông tin đó ra). Trong trường hợp này, việc tìm kiếm và thay thế khá đơn giản:

Nó đòi hỏi một mảng:

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

Và một vòng lặp thông qua các yếu tố đó:

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

Hồ sơ kiểm tra của tôi như sau:

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

Kịch bản đầy đủ của tôi (không có tham chiếu dòng) như sau:

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

Đầu ra của tập lệnh đó dừng trên các sử dụng trái phép trong cùng của dấu ngoặc. Nhưng hãy cẩn thận: 1 / tập lệnh này sẽ không hoạt động với dấu ngoặc trong các nhận xét, biểu thức chính quy hoặc chuỗi, 2 / nó không báo cáo vị trí của tệp gốc, 3 / mặc dù nó sẽ xóa tất cả các cặp cân bằng mà nó dừng ở trong cùng điều kiện lỗi và giữ tất cả các dấu ngoặc.

Điểm 3 / có lẽ là một kết quả có thể khai thác, mặc dù tôi không chắc về cơ chế báo cáo mà bạn có trong đầu.

Điểm 2 / tương đối dễ thực hiện nhưng phải mất hơn một vài phút để sản xuất, vì vậy tôi sẽ để bạn tự tìm hiểu.

Điểm 1 / là điểm khó khăn vì bạn tham gia vào một lĩnh vực hoàn toàn mới về cạnh tranh đôi khi bắt đầu và kết thúc, hoặc quy tắc trích dẫn đặc biệt cho các ký tự đặc biệt ...


1
Cảm ơn, bạn đã cứu tôi. Có một dấu ngoặc không khớp trong tệp json dòng 30k.
I82Much
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.