Як знайти незрівняні дужки у текстовому файлі?


32

Сьогодні я дізнався, що можу використовувати perl -c filenameдля пошуку незрівнянних фігурних дужок {} у довільних файлах, не обов'язково скриптах Perl. Проблема в тому, що вона не працює з іншими типами дужок () [] і, можливо, <>. У мене також були експерименти з декількома плагінами Vim, які стверджують, що допомагають знайти незрівняні дужки, але поки що не дуже добре.

У мене є текстовий файл із досить кількома дужками, і одна з них відсутня! Чи є якась програма / скрипт / плагін vim / що б там не було, що може допомогти мені визначити незрівнянну дужку?

Відповіді:


22

У Vim ви можете скористатися [та ]швидко подорожувати до найближчої незрівняної дужки типу, що вводиться під час наступного натискання клавіш.

Таким чином [{, ви повернетеся до найближчого незрівняного "{"; ])переніс би вас до найближчого неперевершеного ")" тощо.


Чудово, це ідеально для мене. Я збираюся прийняти цю відповідь, але я лише чекаю, чи знайдеться інструмент для обробки тексту, який може проаналізувати це.
phunehehe

6
Я також додам, що у vim ви можете скористатися% (Shift 5, у США), щоб негайно знайти відповідну дужку для тієї, на якій ви перебуваєте.
atroon

@atroon Ооо, приємно. Я ще цього не знав. Я інколи люблю stackexchange. :)
Шадур

це <kbd> [</kbd> і <kbd>] </kbd> справді стрибає на
wirrbel

Я майже день провів 4000 рядків, намагаючись знайти відсутніх} в R, і це була відповідь. Ще раз дякую, ВІМ! Але я думаю, що це хороший аргумент для розбиття файлів вихідного коду на менші шматки.
Томас Браун

7

Оновлення 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

Цей сценарій геніальний!
Джонатан Дюман

1
Це приголомшливо, але, здається, завжди друкується Line, Column (8, 10)незалежно від того, на якому файлі я його спробую. Також mogXCt=${#mogX}встановлений, але ніде не використовується.
Клейтон Герцог

5

Найкращий варіант - vim / gvim, визначений Shadur, але якщо ви хочете сценарію, ви можете перевірити мою відповідь на аналогічне запитання на Stack Overflow . Тут я повторюю всю свою відповідь:

Якщо те, що ви намагаєтеся зробити, стосується мови загального призначення, то це нетривіальна проблема.

Для початку вам доведеться турбуватися про коментарі та рядки. Якщо ви хочете перевірити це на мові програмування, яка використовує регулярні вирази, це знову зробить ваш квест важче.

Тому перш ніж я можу зайти і дати вам будь-яку пораду щодо вашого питання, мені потрібно знати межі вашої проблемної області. Якщо ви можете гарантувати відсутність рядків, зауважень та регулярних виразів, що турбуються - або ще більше ніде в коді, що дужки можуть бути використані інакше, ніж для використання, для якого ви перевіряєте, що вони врівноважені - це буде зробити життя набагато простішим.

Знання мови, яку ви хочете перевірити, було б корисно.


Якщо взяти гіпотезу, що немає шуму, тобто всі дужки є корисними дужками, моя стратегія була б ітеративною:

Я б просто шукав і видаляв усі внутрішні пари дужок: ті, що не містять дужок всередині. Найкраще це зробити, зібравши всі рядки в один довгий рядок (і знайдіть механізм для додавання посилань на рядки, якщо вам потрібно буде отримати цю інформацію). У цьому випадку пошук і заміна досить прості:

Для цього потрібен масив:

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 / є хитрою, тому що ви вводите цілу нову сферу конкуруючих іноді вкладених початків і закінчень або спеціальних правил цитування спеціальних символів ...


1
Спасибі, ти мене врятував. Був один невідповідний дужок у файлі json рядка 30k.
I82Much
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.