bstファイルの編集【bibTeX・スタイルファイル】

TeX

大学で出版している論文集に、参考文献用のbstファイルが用意されていないので、自分で作成することにしました。

元にしたbstファイル

元にしたbstファイルは、TeXに標準で搭載されている次のスタイルファイルです。

  • acm.bst
  • ieeetr.bst

論文集の体裁に一番近かったのが acm.bst だったので、ベースは acm で作成することにしました。足りない部分は別のファイル「ieeetr.bst」からコードを拝借します。幸い、殆ど既存のコードを切り貼りするだけで、欲しいものが得られたので、初心者でもなんとかなりました。

ちなみに元の acm での表示はこんな感じです。

ファイルの場所と著作権について

スタイルファイルはTeXをインストールしたフォルダで、それぞれのファイル名を検索すれば出てきます。例えばtexlive2020だと、次のフォルダに両方とも格納されています。
当たり前ですが、編集するときは元のファイルを壊さないように、複製してから行いましょう。

 (インストールフォルダ)\texlive\2020\texmf-dist\bibtex\bst\base

ちなみに、これらのファイルを開くと、冒頭に著作権に関する記述があり、それによると編集や再配布も認められているとのことです。(ただし編集したりそれを再配布するには、ファイル名を変更する必要があります。)
なのでこれらを編集して、論文集用のbstファイルとするのが良さそう。実際に、そうしている学会のbstファイルもあるようなので、ひとまず編集・再配布しても著作権的には問題なさそうです。

論文名にクォーテーションマーク「“~”」を付ける

acm では論文名にダブルクォーテーションマーク「“ ”」が付いていないので、これを付けます。

ieeetr.bstではクォーテーションマークが付いているので、
format.title関数 を ieeetr のものに差し替えます。

%変更前
FUNCTION {format.title}
{ title empty$
    { "" }
    { title "t" change.case$ }
  if$
}

%変更後
FUNCTION {format.title}
{ title empty$
    { "" }
    { "``" title "t" change.case$ * "''" * }
  if$
}

change.case$ はその手前の文字列 “t” に応じて、title の大文字小文字の変換を行う関数です。
元のコードの手前と後ろに、”“” と 「* ” ” ” *」を加えることで、クォーテーションマークを入力しています。

著者名まわりの編集

acm では著者名のファミリーネームが全て大文字になっているので、これを先頭のみが大文字になるように修正します。
scapify関数で {\sc } が付くようにプログラムされているようなので、format.authorsからscapifyを消去。

FUNCTION {format.authors}
{ author empty$
    { "" }
%    { author format.names scapify } %変更前
	{ author format.names } %変更後
  if$
}

このままでは著者を書き終わった部分にカンマが入らないため、article 関数の定義で author 部分の下のnew.blockを消します。これで著者の後ろにカンマが入ります。
ついでに、論文タイトルの後ろにはピリオドではなく、カンマが入るように、タイトル部分の下の new.block もコメントアウトします。

FUNCTION {article}
{ output.bibitem
  format.authors "author" output.check
%  new.block
  format.title "title" output.check
%  new.block
  crossref missing$
    { format.journal.vol.num.date output
      format.pages output
    }
    { format.article.crossref output.nonnull
      format.pp.pages output
    }
  if$
  new.block
  note output
  fin.entry
}

これは「output.check」の中で動いている「output.nonnull」という関数が上手く機能するためなのですが、その仕組みを解説すると長くなるので別記事にてまとめました。
output.check の詳しい仕組みについては以下の記事を参考にしてください。
 bstファイルの output.check, output.nunnull のコード解説

さらにカンマをクォーテーションマークの内側に入れることもできます。
後の章(タイトルの後ろのカンマをクォーテーションマークの内側に入れる)を参照

ジャーナル名まわりの編集

acm ではジャーナル名がイタリック体なのですが、Vol(巻号)や No までイタリック体になってしまうので、これを修正します。

イタリック体にするコードを出力しているのは、emphasize や emphasizeic関数です。

FUNCTION {emphasize}
{ duplicate$ empty$
    { pop$ "" }
    { "{\em " swap$ * "}" * }
  if$
}

FUNCTION {emphasizeic}
{ duplicate$ empty$
    { pop$ "" }
    { "{\em " swap$ * "\/}" * }
  if$
}

・「duplicate$」はスタック内の一番上の1つを複製してプッシュするコマンド。
 ここでは以下のformat.journal.vol.num.date関数のなかでプッシュされている雑誌名などを想定しています。
・「swap$」はスタック内の上の2つを入れ替えるコマンド。
 ”{\em ” をプッシュした後に、先にプッシュされている雑誌名などとを入れ替えることを想定しています。

この emphasize関数の位置を、format.journal.vol.num.date関数の定義内で移動させることで、イタリック体になる部分を調整できます。
今回は number をスタックした直後に書かれていた emphasize を journal をスタックした直後へ移動します。
ついでにNoのまえに「no.~」を追記しました。

%変更前
FUNCTION {format.journal.vol.num.date}
{ journal empty$
    { "empty journal in " cite$ * warning$
      ""
    }
    { journal
      volume empty$
        'skip$
        { " " * volume * }
      if$
      number empty$
        'emphasizeic %ここのemphasizeicを消して 'skip$に置き換え
        { emphasize ", " * number * } %ここのemphasizeも消す。
      if$
      year empty$
        { "empty year in " cite$ * warning$ }
        { " (" * format.date * ")" * }
      if$
    }
  if$
}

%変更後
FUNCTION {format.journal.vol.num.date}
{ journal empty$
    { "empty journal in " cite$ * warning$
      ""
    }
    { journal emphasize   %ここにemphasizeを追加
      volume empty$
        'skip$
        { ", " * volume * }
      if$
      number empty$
        'skip$
        { ", no.~" * number * }  %ここに「no.~」を追加 
      if$
      year empty$
        { "empty year in " cite$ * warning$ }
        { " (" * format.date * ")" * }
      if$
    }
  if$
}

ページ数に「pp. 」を追記

ページ数の前に「pp. 」が追記されるように「format.pages関数」を修正します。
ieeetr.bst にそのようなプログラムがあったので、ここからコードを移植しました。

このとき、format.pages の定義を単にコピペするだけではだめで、
「INTEGERS { multiresult }」と、multi.page.check 関数の定義もコピペします。
multi.page.checkを挟むことで、ページが複数ある場合は「pp」を、単ページのときは「p」を出力できるようになります。

%変更前
FUNCTION {format.pages}
{ pages empty$
    { "" }
    { "pp. " pages * n.dashify }
  if$
}


%変更後(ieeetrから移植)
INTEGERS { multiresult }

FUNCTION {multi.page.check}
{ 't :=
  #0 'multiresult :=
    { multiresult not
      t empty$ not
      and
    }
    { t #1 #1 substring$
      duplicate$ "-" =
      swap$ duplicate$ "," =
      swap$ "+" =
      or or
        { #1 'multiresult := }
        { t #2 global.max$ substring$ 't := }
      if$
    }
  while$
  multiresult
}

FUNCTION {format.pages}
{ pages empty$
    { "" }
    { pages multi.page.check
        { "pp.~" pages n.dashify * }
        { "p.~" pages * }
      if$
    }
  if$
}

文献の順番をアルファベット順から引用順に変更

文献の順番をアルファベット順から引用順に並べ替えます。
アルファベット順に並べ替えているのは、SORTコマンドと presort 関数(acmの場合)などですので、このあたりを削除するだけです。

コードの後半に 「READ」や「FUNCTION {sortify}」と書かれているところがあるので、ここから、「ITERATE {presort}」「SORT」と書かれている部分を削除します。

%【変更前】
︙
READ

FUNCTION {sortify}

〜〜(中略)〜〜

ITERATE {presort}

SORT

STRINGS { longest.label }
︙


%【変更後】
︙
READ

STRINGS { longest.label }
︙

上の変更を全部やると、出来上がりはこんな感じです。

タイトルの後ろのカンマをクォーテーションマークの内側に入れる

上で説明したタイトルまわりの編集方法だと、
 ”タイトル”, ~
のようにタイトルの後ろに、カンマが出力されます。
(米だと内側、英だと外側が正しいらしい。)

そのため、次のような変更をすることで、ダブルクォーテーションの内側にカンマを入れることができます。

  1. format.title の定義でクォーテーションマークの内側にカンマを入れる。
  2. 「INTEGERS」、「init.state.consts関数」、「output.nonnull関数」
    の3つをieeetr.bst のものに書き換える。
  3. blank.sep 関数の定義を ieeetr から追記する。
  4. article 関数の定義で title 部分の下の 「new.block」 を消して、「blank.sep」 を書きたす。

「1」で ” ” の内側にカンマが入りますが、これだけでは ” ” の後にもカンマが入ってしまうので、「2」~「4」の変更を施します。ieeetr ではクォーテーションマークの内側にカンマは入るようになっているので、そのコードを拝借するのが楽です。

%変更後のformat.title関数の定義
FUNCTION {format.title}
{ title empty$
    { "" }
    { "``" title "t" change.case$ * ",''" * } %カンマを入れる。
  if$
}
%変更後の「INTEGERS」、「init.state.consts関数」、「output.nonnull関数」
%(ieeetr のものから移植)

INTEGERS { output.state before.all mid.sentence after.quote after.sentence
                after.quoted.block after.block }

FUNCTION {init.state.consts}
{ #0 'before.all :=
  #1 'mid.sentence :=
  #2 'after.quote :=
  #3 'after.sentence :=
  #4 'after.quoted.block :=
  #5 'after.block :=
}

STRINGS { s t }

FUNCTION {output.nonnull}
{ 's :=
  output.state mid.sentence =
    { ", " * write$ }
    { output.state after.quote =
        { " " * write$ }
        { output.state after.block =
            { add.period$ write$
              newline$
              "\newblock " write$
            }
            { output.state before.all =
                'write$
                { output.state after.quoted.block =
                    { write$
                      newline$
                      "\newblock " write$
                    }
                    { add.period$ " " * write$ }
                  if$
                }
              if$
            }
          if$
        }
      if$
      mid.sentence 'output.state :=
    }
  if$
  s
}


%以下をieeetrから新しく追記
FUNCTION {blank.sep}
{ after.quote 'output.state :=
}
%変更後のarticle関数 
FUNCTION {article}
{ output.bibitem
  format.authors "author" output.check
%  new.block
  format.title "title" output.check
%  new.block   %new.blockは消す。
  blank.sep   %ここにblank.sepを書き足す。
  crossref missing$
(以下略)

ここで置き換えた「output.nunnull」の詳しい仕組みついてはこちらの記事をご覧ください。元の acm.bst の場合で解説していますが、基本は同じです。
bstファイルの output.check, output.nunnull のコード解説

ざっくりと仕組みを説明するとこうです。
 output.nonnull は output.check の中で核として動いている関数です。この関数が著者名・タイトル・雑誌名などの間で、カンマやピリオド、空白のスペースを出力したりします。多くの条件分岐からなる関数ですが、「output.state」の値に応じて、それらの間に出力するものが決まります。
  ieeetr ではoutput.stateの取りうる値として、「FUNCTION {init.state.consts}」で6つの値が用意されています。元々のacmでは4つの値しか用意されていなかったので、この辺りの書き換えも必要というわけです。
 さらに article の定義で、black.sep をauthorの下に入力することで、output.state が after.quote の値に設定されます。この値のとき、ieeetr の output.nonnull はスペースのみを出力するようにできているので、タイトルのクォーテーションマークの後に、カンマが入らなくなるという仕組みです。

これでカンマもクォーテーションマークの内側に入って完成です。

参考サイト

以下のサイトを参考にさせていただきました。

Bibtexのスタイルファイル:bstファイルの文法
↑bibtexコマンドの文法が非常に分かりやすくまとまっています

英語版の解説(上の記事は以下のサイトの要約とのことです)

Bibtexのスタイルの改変
↑junsrt.bstというbstファイルのコードの解説が分かりやすい

コメント

タイトルとURLをコピーしました