yucatio@システムエンジニア

趣味で作ったものいろいろ

eclipseの警告表示の重大度レベルごとの挙動

前回の記事で、eclipseの警告レベルをどれにするか迷ったので、各重大度レベルでの挙動を確認しました。

yucatio.hatenablog.com

eclipseの警告表示の設定

eclipseを使用していると、黄色の波線と警告マーク(3角形の中に!)が表示されたことがあるでしょう。 これらの設定は、 設定 > Java > コンパイラー > エラー/警告 で変更可能です。

f:id:yucatio:20200110081256p:plain

エラー、警告、情報、無視の違い

エラー/警告の設定では、eclipseが問題を検出したときにの重大度を設定できます。

重大度レベルごとのeclipseの挙動です。

重大度レベル 表示
エラー 問題がある箇所に赤の点線が引かれ、該当する行及びファイル名にバツ(×)印がつきます。さらに、プログラムの実行時に"必要なプロジェクトでのエラー"のダイアログが表示されます。("続行"を押すと実行できます。)
警告 問題がある箇所に黄色の点線が引かれ、該当する行及びファイル名にエクスクラメーションマーク(!)がつきます。実行時にダイアログは出ません。
情報 問題がある箇所に青の波線が引かれ、該当する行及びファイル名に"i"マークがつきます。実行時にダイアログは出ません。
無視 何も表示しません。

エラー

エラーの時の表示です。

f:id:yucatio:20200110081144p:plain

実行時にダイアログが表示されます↓

f:id:yucatio:20200110081158p:plain

警告

警告の時の表示です。

f:id:yucatio:20200110081127p:plain

情報

情報の時の表示です。

f:id:yucatio:20200110081113p:plain

無視

無視の時は何も表示されません。

f:id:yucatio:20200110081101p:plain

エラー、警告、情報の一覧を表示する

現在のワークスペースの警告の一覧を表示するには、ウィンドウ > ビューの表示 > その他 > 一般 > マーカーを開きます。

f:id:yucatio:20200110081050p:plain

"マーカー"のビューに現在のワークスペース内に存在するエラーや警告が表示されます。各項目をクリックすると該当箇所へジャンプすることができます。

f:id:yucatio:20200110081025p:plain

以上、eclipseの警告設定と各重大度レベルごとの挙動でした。

環境

うっかりミスを防ぐeclipseおすすめ設定6つ【後編】

前編に引き続き、うっかりミスを防止(警告)するeclipseの設定をまとめました。

前編の記事

yucatio.hatenablog.com

セミコロン2つを検知

行末のセミコロンを間違って2つ打ってしまうのもありがちなミスです。

設定 > Java > コンパイラー > エラー/警告 > 潜在的なプログラミングの問題 > 空のステートメントの警告レベルを、"情報"にします。

f:id:yucatio:20200107115743p:plain

設定前↓

f:id:yucatio:20200107121016p:plain

設定後↓

f:id:yucatio:20200107121029p:plain

マウスオーバーするとセミコロンの除去がサジェストされます↓

f:id:yucatio:20200107121040p:plain

何も記述されていないブロックを検出

何も記述されていないブロックを検出する方法です。 何も書かれないブロックが必要な場合もあります。そんなときはコメントを書いておくか消しててほしいものです。

設定 > Java > コンパイラー > エラー/警告 > コード・スタイル > 何も記述のない空のブロックの警告レベルを、"警告"にします。

f:id:yucatio:20200107130435p:plain

設定前↓

f:id:yucatio:20200107130540p:plain

設定後↓

f:id:yucatio:20200107130459p:plain

空ブロックにコードかコメントを書くように促されます↓

f:id:yucatio:20200107130553p:plain

@Overrideのつけ忘れを防ぐ

オーバーライドしたメソッドに@Overrideアノテーションが付いていない場合に警告します。

設定 > Java > コンパイラー > エラー/警告 > 注釈 > '@Override' 注釈の欠落の警告レベルを、"警告"にします。

f:id:yucatio:20200107195325p:plain

設定前↓

f:id:yucatio:20200107195355p:plain

設定後↓

f:id:yucatio:20200107195413p:plain

@Overrideをつけるようにとの警告が表示されるようになりました↓

f:id:yucatio:20200107195433p:plain

環境

あとがき

以上、うっかりミスを防ぐためのおすすめeclipse設定でした。書きながら、とりあえず 設定 > Java > コンパイラー > エラー/警告の"無視"となっているところを全て"情報"以上にすればよいのではと思うのでした。

うっかりミスを防ぐeclipseおすすめ設定6つ【前編】

こんにちは。プログラミングでうっかりミスをしてしまうブログ主です。特にスペルミスがひどいです。 レビューで指摘されると申し訳無い気持ちになるので、そのようなうっかりミスを防止(警告)するeclipseの設定をまとめました。

空白の表示

空白タブ、全角空白、改行文字などを表示するオプションです。

設定 > 一般 > エディター > テキスト・エディター > 空白文字を表示にチェックを入れます。

f:id:yucatio:20200107085812p:plain

次に、可視性の構成をクリックして全てチェックを入れます。

f:id:yucatio:20200107085824p:plain

設定前↓

f:id:yucatio:20200107085900p:plain

設定後↓

f:id:yucatio:20200107085911p:plain

タブは^、スペースは.、全角スペースはで表示されました。改行コードは、LF(Unixの改行コード)は、CRLF(Windowsの改行コード)はで表示されます。

チーム開発時にONにしてほしいオプションナンバー1です。空白とタブを混ぜると後々見にくさの原因になります。

スペルチェック

スペルをチェックをするオプションです。

設定 > 一般 > エディター > テキスト・エディター > スペル > スペル・チェックを使用可能にするにチェックを入れます。

f:id:yucatio:20200107091023p:plain

設定前↓

f:id:yucatio:20200107091117p:plain

設定後↓

f:id:yucatio:20200107091131p:plain

赤の波線が出ている箇所にマウスオーバーすると、修正の候補が表示されます↓

f:id:yucatio:20200107091144p:plain

Javadocのチェック

Javadoc@paramなどの項目がメソッドの定義と一致しているかをチェックするオプションです。リファクタリングのときにJavadocを修正し忘れることはよくあるので入れておきたい設定です。

設定 > Java > コンパイラー > Javadocを開き、以下のような設定にします。

f:id:yucatio:20200107113825p:plain

各設定項目の挙動についてはこちらの記事をご覧ください。

yucatio.hatenablog.com

yucatio.hatenablog.com

設定前↓

f:id:yucatio:20200107113839p:plain

設定後↓

f:id:yucatio:20200107113905p:plain

メソッドで宣言されていないパラメータがJavadocにあると警告を出します↓

f:id:yucatio:20200107113920p:plain

returnに何も記述されていないと警告を出します↓

f:id:yucatio:20200107113931p:plain

存在しないクラスやメソッドを参照すると警告を出します↓

f:id:yucatio:20200107113942p:plain

メソッド宣言にあって、Javadocに無いパラメータがあるときに警告を出します↓

f:id:yucatio:20200107113954p:plain

環境

後編に続く

長くなったので続きます。

yucatio.hatenablog.com

Pythonで辞書が格納されたリストを複数のキーで並べ変える

辞書を格納したリストがある時、複数キーで並び変える方法を紹介します。例えば、以下のような辞書が格納された配列で、まずprice順に並び変え、priceが同じだったら、weightの順番に並び変えたい場合の方法です。

list1 = [
    {'name': 'ぶどう',   'price': 5000, 'weight': 1000},
    {'name': 'もも',     'price': 3000, 'weight': 2000},
    {'name': 'りんご',   'price': 5000, 'weight': 5000},
    {'name': 'バナナ',   'price': 1500, 'weight': 1000},
    {'name': 'メロン',   'price': 5000, 'weight': 1200},
    {'name': 'マンゴー', 'price': 10000, 'weight': 900},
    {'name': 'みかん',  'price': 1500, 'weight': 5000},
]

keyにitemgetterを使用する

辞書の値で並び替える場合、itemgetter関数が便利です。

参考: ソート HOW TO — Python 3.8.1 ドキュメント

itemgetterを使用するときは、 パラメータに辞書のキー名を並び替えの優先順位の順に書きます。

sorted(list, key=itemgetter(第1キー, 第2キー, 第3キー))

まずprice順に並び変え、priceが同じだったら、weightの順番に並び変えたい場合はsorted関数のキーにitemgetter('price', 'weight')を指定します。

コードと並べ替えの結果です。

from operator import itemgetter

# list1は上記のlist1と同様
list1 = [...]

# 並べ替え
sorted_list = sorted(list1, key=itemgetter('price', 'weight'))

# 出力
print(f"{'name':<8}{'price':>8}{'weight':>8}")
print('-' * 24)
for item1 in sorted_list:
    print(f"{item1['name']:<8}{item1['price']:>8}{item1['weight']:>8}")

# name       price  weight
# --------------------------------
# バナナ         1500    1000
# みかん         1500    5000
# もも          3000    2000
# ぶどう         5000    1000
# メロン         5000    1200
# りんご         5000    5000
# マンゴー       10000     900

priceで最初に並べ替え、priceが同じ場合はweightで並び替えられています。デフォルトなのでいずれも昇順で並び替えられています。

keyにlambda関数を使用する

より一般的に複数のキーで並び替えたいときは、keyに指定する関数の戻り値にタプルを指定します。

例えば、グラム単価(price/weight)で並び替えた後、同じグラム単価であれば、priceで並び替えるようなプログラムは以下になります。

# list1は上記のlist1と同様
list1 = [...]

# 並べ替え
sorted_list = sorted(list1, key=lambda item: (item['price'] / item['weight'], item['price']))

# 出力
print(f"{'name':<8}{'price(1g)':>10}{'price':>8}{'weight':>8}")
print('-' * 34)
for item1 in sorted_list:
    print(f"{item1['name']:<8}{item1['price'] / item1['weight']:>10.2f}{item1['price']:>8}{item1['weight']:>8}")

# name     price(1g)   price  weight
# ----------------------------------
# みかん           0.30    1500    5000
# りんご           1.00    5000    5000
# バナナ           1.50    1500    1000
# もも            1.50    3000    2000
# メロン           4.17    5000    1200
# ぶどう           5.00    5000    1000
# マンゴー         11.11   10000     900

グラム単価で並び替えた後、同じグラム単価であれば、priceで並び替えることができました。 マンゴーのグラム単価高いですね。

以上でPythonで辞書が格納されたリストを複数のキーで並べ変えることができました。

環境

htmlのテーブルに使われるタグが覚えられないと思ったときに読む記事

こんにちは。htmlのtable関連タグが覚えられないブログ主です。特に<tr><td>がごちゃごちゃです。が、それぞれの何の英単語の略であるかを覚えたところ、ごちゃごちゃにならずにすみました。今回はそれぞれ何の英語の略なのかと、日本語訳を紹介します。

テーブルの略語タグの略さない名称と日本語での解説

タグ名 何の略か 解説
tr Table Row テーブルの横の行。"row"は"横の行"のこと。
th Table Head テーブルの見出し。"head"は"頭"の意味もありますが、ここでは"見出し"の意味です。
td Table Data テーブルの(個々の)データ。

f:id:yucatio:20200104173533p:plain

rowspanとcolspan

テーブルのセルを結合するrowspanとcolspanも覚えにくいですね。colspanは"column span"の略です。

横の行のことを"row"といい、縦の列を"column"といいます。"row"は"人が並んだ列"の意味もあります。横のイメージですね。"column"は"柱"という意味があります。こちらは縦のイメージですね。

"span"は、"長いスパンで考える"などで使われる"スパン"です。"期間"という意味でも使われますが、htmlの属性では、"(端から端までの)全長"という意味で使用されています。が、全長だと長い方をさす方が多いので、ここでは"太さ"という略語をあてます。

例えば、rowspan="2"であれば、行の太さが2なので、縦に連結されます。

また、colspan="3"であれば、列の太さが3なので、横に連結されます。

f:id:yucatio:20200104173218p:plain

上記テーブルのソースコードはこちらです。

<!DOCTYPE html>
<html>
  <head>
    <meta charset="utf-8">
    <title>テーブルタグ確認</title>
    <style type="text/css">
      table {border-collapse: collapse; margin: 40px 20px;}
      th, td {border:1px solid #333; padding: 3px 5px;}
      td.number {text-align:right;}
      thead {background-color: #7fbfff; color: #fff;}
      tfoot {background-color: #eee;}
    </style>
  </head>
  <body>
    <table>
      <thead>
        <tr>
          <th>品名</th><th>単価</th><th>数量</th><th>金額</th><th>消費税</th>
        </tr>
      </thead>
        <tr>
          <td>鉛筆</td><td class="number">30</td><td class="number">12</td><td class="number">360</td><td rowspan="2" class="number">10%</td>
        </tr>
        <tr>
          <td>消しゴム</td><td class="number">50</td><td class="number">6</td><td class="number">300</td>
        </tr>
      <tbody>
      </tbody>
      <tfoot>
        <tr>
          <td colspan="3">合計</td><td class="number">660</td><td></td>
        </tr>
        <tr>
          <td colspan="3">消費税</td><td class="number">66</td><td></td>
        </tr>
        <tr>
          <td colspan="3">合計</td><td class="number">726</td><td></td>
        </tr>
      </tfoot>
    </table>
  </body>
</html>

あとがき

htmlのタグや属性には他にも英語を略したものが使われているので、調べてみると覚えやすくなると思います。

Pythonのconfigparserでセクションがない設定ファイルを(無理矢理)読む

背景

PythonJavaのMANIFEST.MFファイルを読んで表示するプログラムを書く必要がありました。

MANIFEST.MFファイルは以下のような形式です。

Manifest-Version: 1.0
Built-By: yucatio
Build-Jdk: 11.0.5
Created-By: Maven Integration for Eclipse
Implementation-Branch: feature/TOTTEMO_NAGAI_BRANCH_NAME/LONG_SUB_DIRECT
 ORY
Implementation-Build: 1.2.3
  • キー: バリューのようにセミコロンを区切り文字にしている。セミコロンの後には空白が1つ続くが、この空白は値には含まれない。
  • キーの命名規則はChain-Caseで各単語の先頭は大文字
  • 各行は72バイトまでで、それより長くなるときは、改行し行頭に1つ空白を開けてから続きを書く

この形式のファイルをPythonのconfigparserで読んでみます。

コード

ファイルパスを引数にとり、設定内容を辞書型で返すプログラムとその使い方です。

import configparser


def read_config_without_section(config_path):
    """引数に指定されたconfigファイルの内容をdict形式で返す"""
    with open(config_path, 'r') as f:
        # ファイルの内容の先頭に'[dummy_section]'をつける
        config_string = '[dummy_section]\n' + f.read()
    config = configparser.ConfigParser()
    config.read_string(config_string)

    # valueの改行コードを削除
    return {key: value.replace('\n', '') for key, value in config.items('dummy_section', raw=True)}


def main():
    properties = read_config_without_section('/path/to/MANIFEST.MF')

    for key, value in properties.items():
        print(f'{key}:{value}')

# [出力]
# manifest-version:1.0
# built-by:yucatio
# build-jdk:11.0.5
# created-by:Maven Integration for Eclipse
# implementation-branch:feature/TOTTEMO_NAGAI_BRANCH_NAME/LONG_SUB_DIRECTORY
# implementation-build:1.2.3

キーが全て小文字になることに注意が必要ですが、上記のMANIFEST.MFの内容が取得できることがわかりました。

コードの解説

MANIFEST.MFファイルのキーと値を読み込むときに、自前でsplit関数などを使用して自前で読みこむことも考えましたが、configparserのマルチライン機能が便利なのでこちらを使用することにしました。

configparser

configparserの公式ドキュメントはこちらです。使用する際はimportが必要です。

configparser --- 設定ファイルのパーサー — Python 3.7.6 ドキュメント

iniファイル

configparserは iniファイルを読み込むことを想定しています。iniファイルは以下のようにセクションヘッダ([]で囲まれた見出し)とそれに続くkey=valueで構成されます。

iniファイルの例

[Mail]
subject=Sample Mail
mail_from=noreply@yucatio.com

[Log]
log_dir=./log
log_prefix=application
log_rotation=daily

今回読み込もうとするファイルにはセクションヘッダがありません。 セクションヘッダがないファイルを以下のように読み込もうとすると

def read_config_without_section(config_path):
    config = configparser.ConfigParser()
    config.read(config_path)


def main():
    properties = read_config_without_section('/path/to/MANIFEST.MF')

以下のようなエラーが出ます。

# 前略
File "/usr/local/Cellar/python/3.7.5/Frameworks/Python.framework/Versions/3.7/lib/python3.7/configparser.py", line 1079, in _read
    raise MissingSectionHeaderError(fpname, lineno, line)
configparser.MissingSectionHeaderError: File contains no section headers.
file: '/path/to/MANIFEST.MF', line: 1
'Manifest-Version: 1.0\n'

そこで、ダミーのセクションヘッダをつけます。一旦ファイルの内容を全て読み込み、先頭に[dummy_section]と改行を付けます。

    with open(config_path, 'r') as f:
        # ファイルの内容の先頭に'[dummy_section]'をつける
        config_string = '[dummy_section]\n' + f.read()

configオブジェクトには、read_stringという、文字列を受け取って設定データを読むメソッドがあります。こちらを利用します。

    config = configparser.ConfigParser()
    config.read_string(config_string)

これで、設定が読み込まれました。

キーとバリューの区切り文字

configparserでは、デフォルトでは=または:が区切り文字です。最初に出てきた区切り文字で区切られます。今回はデフォルトのまま使用します。

値のtrim

設定の読み込みの際、キーと値、それぞれの先頭と末尾の空白は取り除かれます。今回はこの性質があるので自分でtrimを書かずに済みました。

マルチライン対応

configparserでは、マルチラインが扱えます。 値の2行目以降は、キーよりも深くインデントする必要があります。幸い、今回読み込むファイルは、2行目が1つの空白でインデントされています。 複数行の値は、読み込まれるとき、2行目以降の先頭の空白は取り除かれ、各行の間には改行コードが入ります。

# 例えば、
key1=value1
  value2
# のとき、key1の値は
# "value1\nvalue2"
# になる(\nは改行を表す)

今回は、値が複数行に渡るときには改行は必要ないので、削除します。

まず、config.items('dummy_section', raw=True)で設定の内容を得ます。このメソッドは、dictのitems()と同様に'dummy_section'セクションの内容をタプルで返します。raw=Trueで変数展開をしないようにします。

タプルの各値をkey, valueで受け取り、value.replace('\n', '')で改行コードを削除しています。

# valueの改行コードを削除
return {key: value.replace('\n', '') for key, value in config.items('dummy_section', raw=True)}

以上でPythonのconfigparserでセクションがない設定ファイルを(無理矢理)読むことができました。

環境

参考

エラーメッセージ内では、"登録できる画像は3つまで"のように"つ"でものを数えてはいけない

仕事で印象に残っている小ネタ。 普段はEC関連のシステムエンジニアをしています。とあるアプリをチームメンバーが改修した時の話です。

改修内容

担当しているサービスで、商品画像の上限枚数を5枚から12枚に変更する改修をしました。 私のチームが担当しているシステムは商品の登録システムでした。ストレージの見積もり等の作業はありますが、 ソースコードの改修は枚数の上限値を変更するだけの簡単なものです。

変更内容

エラーメッセージはファイルに記載されています。こんな感じで変数を埋め込めるようになっています。(実際のものとは違います)

登録できる画像は{MAX_IMAGE}つまでです

ソースコードの改修もこの部分だけでした。

-MAX_IMAGE=5
+MAX_IMAGE=12

テスト

一応テストケースも確認しておきましょう。

-登録できる画像は5つまでです
+登録できる画像は12つまでです

5が12になってる。大丈夫、って"12つ"ってなんだ!?そんな日本語あったっけ?いや、ない。あれ?

1つ、2つ、3つ、4つ、5つ、6つ、7つ、8つ、9つ

うん、ここまでは聞いたことある。

10つ

いや、"じゅっつ"なんて日本語は聞いたことがないぞ。"とお"だろう、ここは。

11つ、12つ

これもないはず。あってるよね?と思ってググってみた。

つ: 1から9までのものに対してつける。10は「とお」で「つ」をつけない。11以上は助数詞「個」などを使う。

wikipediaより

https://ja.wikipedia.org/wiki/%E5%8A%A9%E6%95%B0%E8%A9%9E

大丈夫だ。自分の感じたことがあってる。

というわけで、"つ"を他の言葉に変えなければいけないのだけれども、画像ってなんて数えるのだろう?"個"?"枚"?

がぞう【画像】

一枚(いちまい)、一点(いってん)

https://www.sanabo.com/kazoekata/ct_ka/ka/gazou/

というわけでこの時は"枚"で数えるように変更しました。

このとき、ソースコード改修を担当していたのは日本語ネイティブでないメンバーでした。そりゃ気づけないよね。

こんなことがあったのでちょっとバタバタしましたがリリースは予定通りうまく行きました。

正直テストコードに助けられました。変な日本語でリリースしなくてよかった。

教訓

エラーメッセージ内で何かを数えるとき、単位(助数詞)に"つ"を使わない。