Comment trouver des crochets sans correspondance dans un fichier texte?


32

Aujourd'hui, j'ai appris que je pouvais utiliser perl -c filenamepour trouver des accolades {} incomparables dans des fichiers arbitraires, pas nécessairement des scripts Perl. Le problème est que cela ne fonctionne pas avec d'autres types de crochets () [] et peut-être <>. J'ai également eu des expériences avec plusieurs plugins Vim qui prétendent aider à trouver des brackets incomparables, mais pas très bons jusqu'à présent.

J'ai un fichier texte avec pas mal de crochets et l'un d'entre eux manque! Existe-t-il un programme / script / plug-in vim / quoi que ce soit qui puisse m'aider à identifier le support inégalé?

Réponses:


22

Dans Vim, vous pouvez utiliser [et ]vous déplacer rapidement vers le support le plus proche du type entré dans la frappe suivante.

Donc [{, vous ramènera au "{" inégalé le plus proche; ])vous emmène au plus proche ")" inégalé, et ainsi de suite.


Génial, c'est parfait pour moi. Je suis sur le point d'accepter cette réponse, mais j'attends seulement de voir s'il existe un outil de traitement de texte pouvant analyser cela.
Phunehehe

6
J'ajouterai également que dans vim, vous pouvez utiliser% (Shift 5, aux États-Unis) pour trouver immédiatement le support correspondant à celui sur lequel vous êtes.
atroon

@atroon Ooo, gentil. Je ne le savais pas encore moi-même. J'adore stackexchange parfois. :)
Shadur

<kbd> [</ kbd> et <kbd>] </ kbd> sautent vraiment à la
wirrbel

J'ai passé presque une journée à parcourir 4000 lignes à la recherche des disparus} dans R et c’était la réponse. Encore une fois, merci VIM! Mais je pense que c’est un bon argument pour scinder les fichiers de code source en fragments plus petits.
Thomas Browne

7

Mise à jour 2:
le script suivant affiche maintenant le numéro de ligne et la colonne d'un crochet mal placé . Il traite un type de support par scan ( par exemple. « [] » « <> » « {} » « () » ...)
Le script identifie le premier , support droit sans égal , ou la première d'un crochet gauche jumelé non ... lors de la détection d'un erroe, il se termine par les numéros de ligne et de colonne

Voici un exemple de sortie ...


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

Voici le script ...


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

Ce script est génial!
Jonathan Dumaine

1
C’est génial, mais cela semble toujours imprimer Line, Column (8, 10)quel que soit le fichier sur lequel je l’essaie. ÉgalementmogXCt=${#mogX} est défini mais pas utilisé n'importe où.
Clayton Dukes

5

La meilleure option est vim / gvim, telle qu'identifiée par Shadur, mais si vous voulez un script, vous pouvez vérifier ma réponse à une question similaire sur Stack Overflow . Je répète toute ma réponse ici:

Si ce que vous essayez de faire s’applique à un langage généraliste, il s’agit là d’un problème non trivial.

Pour commencer, vous devrez vous soucier des commentaires et des chaînes. Si vous voulez vérifier ceci sur un langage de programmation qui utilise des expressions régulières, cela rendra votre quête encore plus difficile.

Donc, avant que je puisse entrer et vous donner un conseil sur votre question, je dois connaître les limites de votre zone de problème. Si vous pouvez vous assurer qu'il n'y a pas de chaînes, pas de commentaires et pas d'expressions régulières à craindre - ou plus génériquement nulle part dans le code, les crochets ne peuvent éventuellement être utilisés que pour les utilisations pour lesquelles vous vérifiez qu'ils sont équilibrés - rendre la vie beaucoup plus simple.

Connaître la langue que vous souhaitez vérifier serait utile.


Si je prends l'hypothèse qu'il n'y a pas de bruit, c'est-à-dire que tous les crochets sont des crochets utiles, ma stratégie serait itérative:

Je voudrais simplement rechercher et supprimer toutes les paires de crochets intérieurs: ceux qui ne contiennent pas de crochets à l'intérieur. Pour ce faire, il est préférable de réduire toutes les lignes en une seule ligne longue (et de rechercher un mécanisme permettant d’ajouter des références de ligne si vous devez extraire ces informations). Dans ce cas, la recherche et le remplacement sont assez simples:

Cela nécessite un tableau:

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

Et une boucle à travers ces éléments:

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

Mon fichier de test est comme suit:

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

Mon script complet (sans référencement de ligne) est le suivant:

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

La sortie de ce script s’arrête sur les utilisations illégales les plus profondes des crochets. Mais méfiez-vous: 1 / ce script ne fonctionnera pas avec des crochets dans les commentaires, les expressions régulières ou les chaînes de caractères, 2 / il ne signale pas où se trouve le problème dans le fichier d'origine, 3 / bien qu'il supprime toutes les paires équilibrées, il s'arrête au plus profond conditions d'erreur et conserve tous les supports englobants.

Le point 3 / est probablement un résultat exploitable, bien que je ne sois pas sûr du mécanisme de rapport que vous aviez en tête.

Le point 2 / est relativement facile à mettre en œuvre mais nécessite plus de quelques minutes de travail, je vous laisse le soin de le déterminer.

Le point 1 / est délicat, car vous entrez dans un tout nouveau royaume de débuts et de fins concurrents parfois imbriqués, ou de règles de citation spéciales pour les caractères spéciaux ...


1
Merci, vous m'avez sauvé. Avait un support mal assorti dans un fichier json de 30 000 lignes.
I82Much
En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.