텍스트 파일에서 일치하지 않는 괄호를 찾는 방법은 무엇입니까?

텍스트 파일에서 일치하지 않는 괄호를 찾는 방법은 무엇입니까?

perl -c filename오늘 저는 반드시 Perl 스크립트가 아닌 모든 파일에서 일치하지 않는 중괄호 {}를 찾을 수 있다는 것을 배웠습니다 . 문제는 다른 유형의 대괄호() [] 또는 심지어 <>에서는 작동하지 않는다는 것입니다. 또한 일치하지 않는 대괄호를 찾는 데 도움이 된다고 주장하는 몇 가지 Vim 플러그인을 실험했지만 지금까지 결과는 그다지 좋지 않았습니다.

괄호가 많은 텍스트 파일이 있는데 그 중 하나가 누락되었습니다! 일치하지 않는 괄호를 식별하는 데 도움이 되는 프로그램/스크립트/vim 플러그인 등이 있습니까?

답변1

Vim에서는 다음 키 입력 시 입력된 가장 가까운 일치하지 않는 괄호를 사용하여 빠르게 이동할 [수 있습니다.]

따라서 [{가장 가까운 일치하지 않는 "{"로 돌아가게 되며, ])이는 가장 가까운 일치하지 않는 ")"로 돌아가게 됩니다.

답변2

업데이트 2:
다음 스크립트는 이제 일치하지 않는 행 번호와 열을 인쇄합니다.괄호. 스캔당 하나의 대괄호 유형을 처리합니다(예: "[]" "<>" "{}" "()"...).
스크립트는 다음을 식별합니다.첫 번째,일치하지 않는 닫는 괄호또는 첫 번째짝이 없는 왼쪽 대괄호...오류가 감지되면 행 및 열 번호와 함께 종료됩니다.

다음은 몇 가지 샘플 출력입니다.


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

답변3

가장 좋은 옵션은 확실히 Shadur의 vim/gvim이지만 스크립트를 원한다면 확인할 수 있습니다.내 대답유제스택 오버플로. 여기에 전체 답변을 반복합니다.

당신이 하려는 일이 보편적인 언어에 적용된다면 이는 작은 문제가 아닙니다.

먼저, 주석과 문자열에 대해 걱정해야 합니다. 정규식을 사용하는 프로그래밍 언어에서 이를 확인하려는 경우 작업이 다시 더 어려워집니다.

따라서 제가 귀하의 문제에 대해 조언을 제공하기 전에 귀하가 겪고 있는 문제 영역의 한계를 이해해야 합니다. 걱정할 문자열, 주석, 정규 표현식이 없다는 것을 보장할 수 있다면 - 또는 더 일반적으로 균형이 맞는지 확인하려는 목적을 제외하고는 코드의 어느 곳에서도 대괄호가 사용되지 않습니다. 생활이 훨씬 단순해졌습니다.

확인하려는 언어를 아는 것이 도움이 됩니다.


노이즈가 없다고 가정하면, 즉 모든 대괄호가 유용한 대괄호라면 내 전략은 반복됩니다.

내부에 브래킷이 포함되지 않은 내부 브래킷 쌍을 모두 찾아 제거하면 됩니다. 모든 행을 하나의 긴 행으로 축소하는 것이 가장 좋습니다(해당 정보를 가져와야 하는 경우 행 참조를 추가하는 메커니즘을 찾으세요). 이 경우 검색 및 바꾸기는 매우 간단합니다.

배열이 필요합니다.

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/ 균형 잡힌 쌍을 모두 제거하더라도 가장 안쪽 중지는 수행됩니다. 오류 조건을 유지하고 모든 괄호를 유지합니다.

포인트 3/은(는) 어떤 보고 메커니즘을 염두에 두고 있는지 잘 모르겠지만 악용 가능한 결과일 수 있습니다.

포인트 2/는 구현하기가 상대적으로 쉽지만 완료하는 데 몇 분이 걸리므로 알아보는 것은 여러분의 몫입니다.

포인트 1은 까다롭습니다. 때로는 중첩된 시작과 끝이 경쟁하거나 특수 문자에 대한 특수 인용 규칙이 있는 완전히 새로운 영역에 진입하기 때문입니다.

관련 정보