Pythonでコメント付きJSONやCSVを使う


初投稿です。

JSONを編集しているときに、コメントをつけれたら、わかりやすくてミスも減るだろうと思ったので、Pythonでコメント付きのJSONを開くプログラム(というよりコメントを削除するプログラム)を作ってみました。
やっていることは、コメントの削除(ダブルクォーテーション内のコメントの記号は無視)をしたあと読み込むだけなので、CSVにも応用できます。

コメントの書き方は、C言語やC++と同じです。
ラインコメント　→ //ライン
ブロックコメント　→  /* ブロック */

追記: コメントできる**YAML**というデータフォーマットがあるそうなので、それも選択肢の一つです。
#コード
文字列を一文字づつとってきて、コメント内なのか判定しています。ダブルクォーテーションに囲まれたコメント記号は無視することもできます。フラグをいっぱい使ってます。バグがあったら教えてください。

```python
"""
コメントアウトするメソッド
一文字づつ処理して、コメント文は削除する
input:
    string: コメントを含んだ文字列
    quot_invalid: ダブルクォーテーションに囲まれた箇所のコメント記号を無視する
output: コメントを削除した文字列
"""
def comment_out(string, quot_invalid=True):
    o_s = "" #コメントを削除した文字列
    slash_start_flag = False #前の文字がスラッシュであるフラグ
    slash_asterisk_delete_flag =  False #ブロックコメント内であるフラグ
    slash_asterisk_end_flag = False #ブロックコメント内で前の文字がアスタリスクであるフラグ
    double_slash_delete_flag = False #ラインコメント内であるフラグ
    in_dq_flag = False #ダブルクォーテーション内であるフラグ
    for c in string:
        if in_dq_flag and quot_invalid:
            o_s += c
            if c == '"':
                in_dq_flag = False
        elif slash_asterisk_end_flag:
            if c == "/":
                slash_asterisk_delete_flag = False
            slash_asterisk_end_flag = False
            if c == "*":
                slash_asterisk_end_flag = True
        elif slash_asterisk_delete_flag:
            if c == "*":
                slash_asterisk_end_flag = True
        elif double_slash_delete_flag:
            if c == "\n":
                o_s += "\n"
                double_slash_delete_flag = False
        else: #コメント内ではないとき、または、コメントの始まりの'/'や'*'
            if slash_start_flag:
                if c == "*":
                    slash_asterisk_delete_flag = True
                elif c == "/":
                    double_slash_delete_flag = True
                else:
                    o_s += "/" + c
                    if quot_invalid and c == '"':
                        in_dq_flag = True
                slash_start_flag = False
            elif c == "/":
                slash_start_flag = True
            else:
                o_s += c
                if quot_invalid and c == '"':
                    in_dq_flag = True
    #空白行を削除
    return '\n'.join(filter(lambda x: x.strip(), o_s.split('\n')))

```
##正規表現を使ったコード
コメントで教えてもらったコードです。正規表現を使っていてコードが短くて理解しやすいです。上のメソッドのquot_invalidがFalseの場合と同じ出力を返します。ダブルクォーテーション内のコメント記号は無視されません。

```python
import re

def comment_out(string):
    # ブロックコメント除去
    string = re.sub(r'/\*.*?\*/', r'', string, flags=re.DOTALL)

    # ラインコメント除去
    string = re.sub(r'//.*\n', r'\n', string)
    return '\n'.join(filter(lambda x: x.strip(), string.split('\n')))
```

#実行結果
実行例です。comment_outを使うとコメントが消えます。ここでは第二引数にFalseを入れたときの結果です。

```Python
string = '''/*
JSONのサンプルです
*/
{
    "name":"asasan",//名前です
    "comment_test":"ここは残す/*ここは削除*/"
}
'''
print(comment_out(string,quot_invalid=False))


====実行結果====

{
    "name":"asasan",
    "comment_test":"ここは残す"
}
```

上の列では、ダブルクォーテーションで囲まれた箇所のコメント記号も無視されずに削除されました。ダブルクォーテーションで囲まれた箇所のコメント記号を無視する場合は第二引数にTrueを入れます。デフォルトではTrueです。

```python
string = '''/*
JSONのサンプルです
*/
{
    "name":"asasan",//名前です
    "comment_test":"ここは残す/*ここも残す*/"
}
'''
print(comment_out(string,quot_invalid=True))

====実行結果====

{
    "name":"asasan",
    "comment_test":"ここは残す/*ここも残す*/"
}
```

ファイルから読み込み時は、下のようにすればjson_dataに値が格納されます。

```python
import json
f = open('sample.json')
string = f.read()
f.close()
json_data = json.loads(comment_out(string))
```