一致しない区切り文字を含む行を検索する

一致しない区切り文字を含む行を検索する

実行すると競合が発生pdflatexし、私の.auxファイルには次の行が含まれています。

\@writefile{toc}{\contentsline {section}{\numberline {B

そのような行を識別するために私が考えることができる唯一の方法は、数が行の数を超えるかどうかを計算することです{。生成されたファイルを調べて、その行が含まれていることを確認したいと}思います。または、他のユーティリティを使用してこれを行う方法はありますか?もちろん、これらの線を識別するより効率的な選択肢があればうれしいです。.auxpdflatexgrepawk

どんな提案でも送ってくれてありがとう

答え1

もう一つの短い内容は次のとおりです。

awk '{while(gsub(/{[^{}]*}/, "")){ }} /[{}]/ {exit 1}'

または多分

awk '{x=$0;while(gsub(/{[^{}]*}/, "")){ }} /[{}]/ {print FILENAME,FNR,x;nextfile}'

これにより、バランスの取れたすべてのアイテムが削除されたり、文字がまだ存在する{...}場合はいくつかのアクションが実行されます。{}

答え2

はい、grep(PCREを使用)では可能で非常に正確ですが、理解するのは簡単ではありません。

grep -Px '((?>[^{}]+|\{(?1)\})*)'

あるいは、入力($str)と適切な正規表現($re)を定義するには、次のようにします。

$ printf '%s\n' "$str" | grep -vP "${re//[ $'\n']/}"

どのように動作しますか?

これで、正規表現はバランスの取れた構成と一致します(ほとんどの古い正規表現エンジンではありません)。

PCREでは、再帰がこれを達成する鍵です。

到着バランスの取れたセットマッチ次の構造が必要です。

b(m|(?R))*e

b開始パターンはどこにあり({あなたの場合)、
e終了パターンはどこにあり(}あなたの場合)、
中間mパターンはどこにあります(あなたの場合と似ています[^{}]+)。

{([^{}]*+|(?R))*}

おそらくここで仕事を確認してください。

ただし、これは固定されていない一致なので、正規表現(?R)全体に対して繰り返されます。

固定バージョン(全行と一致)はgrepオプションを使用して取得できます-x

中括弧の外側に追加のテキストを許可する完全な解決策はもう少し複雑になるため、Perl正規表現オプションを使用してスペースを無視できます。正規表現構造を次のように変更します(少し遅い)。

((m+|b(?1)e)*)

元の構造b(m|(?R))*e

(?(DEFINE)(?'nonbrace'  [^{}\n]       ))  # Define a non-brace
(?(DEFINE)(?'begin'     {             ))  # Define the start text
(?(DEFINE)(?'end'       }             ))  # define the end text 
(?(DEFINE)(?'middle'    (?&nonbrace)  ))  # define the allowed text
                                          # inside the braces

(?(DEFINE)(?'nested'                            # define a nested
    ((?&begin)((?&middle)|(?&nested))*(?&end))  # pattern
  ))                                            # here

^((?&nonbrace)*+(?&nested))*+(?&nonbrace)*$     # finally, use this regex.

〜のようにここでテスト済み

または代替構造 ((m+|b(?1)e)*)

(?(DEFINE)(?'nonbrace'  [^{}\n]       ))  # Define a non-brace
(?(DEFINE)(?'begin'     \{            ))  # Define the start text
(?(DEFINE)(?'end'       \}            ))  # define the end text 
(?(DEFINE)(?'middle'    (?&nonbrace)  ))  # define the allowed text
                                          # inside the braces

(?(DEFINE)(?'nested'                             # define a nested
     (  (?&middle)++  |  (?&begin)(?&nested)(?&end)  )*
))

^(?&nested)$     # finally, use this regex.

〜のようにここでテスト済み

DEFINE が多い非常に長い正規表現が正規表現エンジンによってコンパイルされると、より短い正規表現と同じ速度で動作します。

追加された機能は、説明が人間にとってより明確であることです(または少なくともそれを願っています)。

これは通常、人間が理解しやすいが、PCREのかなり深い正規表現機能を使用する正規表現のより明確な説明を示しています。

スクリプト

grep(GNU および PCRE) でこれらすべてのアイデアを使用するには、次のシェルの例を使用します。

#!/bin/bash

str=$'
a
abc
{}
{a}
{{aa}}
{a{b}}
{a{bb}a}
{a{b{c}b}a}
n{a{}}nn{b{bb}}
\@writefile{toc}}}}{\\contentsline {section}{\\numberline {B
\@writefile{toc}{\contentsline {section}{\\numberline {B
Previous lines contain mismatched braces. This and the next line don\'t.
\@writefile{toc}{\\contentsline {section}{\\numberline {B}}}
'

re=$'                    
  (?(DEFINE)(?\'nonbrace\'  [^{}\\n]      ))
  (?(DEFINE)(?\'begin\'     {             ))
  (?(DEFINE)(?\'end\'       }             ))
  (?(DEFINE)(?\'middle\'    (?&nonbrace)  ))
  (?(DEFINE)(?\'nested\'
      ((?&begin)((?&middle)|(?&nested))*(?&end))
    ))
  ^((?&nonbrace)*(?&nested))*(?&nonbrace)*$
'

printf '%s\n' "$str" | grep -P "${re//[ $'\n']/}"

a
abc
{}
{a}
{{aa}}
{a{b}}
{a{bb}a}
{a{b{c}b}a}
n{a{}}nn{b{bb}}
Previous lines contain mismatched braces. This and the next line don't.
\@writefile{toc}{\contentsline {section}{\numberline {B}}}


試験結果

最後に、一致しないすべての行を取得するには、出力を逆にします-v(実行中のシェルで次を実行する必要がある場合は上記のスクリプトを取得します)。

$ printf '%s\n' "$str" | grep -vP "${re//[ $'\n']/}"

\@writefile{toc}}}}{\contentsline {section}{\numberline {B
\@writefile{toc}{ntentsline {section}{\numberline {B

答え3

sed@rowboatメソッドの翻訳awk

sed 'h; s/[^{}]//g; :1
     s/{}//g; t1
     /./!d; g'

それは:

sed '
  h; # save a copy of the line on the hold space
  s/[^{}]//g; # remove all characters but { and }
  :1
    s/{}//g; # remove the {}s (so starting with inner ones)
  # and loop until there's no more {} to remove
  t1

  /./!d; # if the pattern space does not contain any single
         # character, that means all {} were matched. Delete

  g; # otherwise retrieve the saved copy which will be printed
     # at the end of the cycle'

これはPOSIXですが、awkPerlなどの再帰正規表現を使用するソリューションよりもはるかに遅いです。

grep -Pvx '((?:[^{}]++|\{(?1)\})*+)'

答え4

使用awk:

  • 各レコードについて、合計はゼロに初期化されます。
  • 1行ずつ1行ずつスキャンを開始します。
  • 開いている中かっこを最初に表示するときに合計を増やし、閉じる中かっこを表示するときに合計を減らします。
  • 合計がゼロ以下に下がったら停止します。
  • forループの終わりに達すると、負の合計のために中間にあるか、通常は合計がゼロでない場合、ゼロ以外の状態で終了します。
  • 注:この方法は中括弧の数を計算するのとは異なります。ここでは、合計が負になると処理を停止します。
awk 'BEGIN { a["{"]=1;a["}"]=-1 }
{ for (s=i=0; i++<length();) if (0>(s += a[substr($0,i,1)])) break }
s {exit 1}' file

同じものperl

perl -lne '
  local(%h,$^R) = qw/{ 1 } -1/;
  /(?:(?:([{}])(?{$^R+=$h{$1}})|[^{}]+)(?(?{$^R<0})(?!)))+/g;
  exit 1 if $^R;
' file

Perlは、独自のミニプログラミング言語とほぼ同様の強力な正規表現機能を備えています。正規表現内でループを実行し、合計を更新し、合計がゼロより下がる時点を監視します。

関連情報