LoginSignup
0
1

More than 3 years have passed since last update.

投稿記事の Markdown 中のコードをファイルとして取り出す

Last updated at Posted at 2021-01-24

Qiita は、全体は小さいけど、多数のファイルで構成されたソースコードを記事に入れようと思うと

  • 記事にするときも手間
  • 利用するときも手間

なので、そういうものは記事にしにくい側面があります。そこで、(Qiita で完結する前提で)手間を軽減したいわけですが、ファイルから Markdown にするものと、その逆があれば少しは良くなるでしょう。既に誰かやっててもよさそうな気がしますが、探せなかったので作ることにしました。

変換処理は

  • ファイルから Markdown へ

    • テキスト ファイルの前後に「```」の行を追加
      • ファイルの種類に応じたコードの挿入
        • 種類の判定が必要
      • ファイル内行頭「```」
        • 元のコードをうまく表示する法方を見つけられなかったので「\」を先頭に追加
  • Markdown からファイルへ

    • Markdown の「```」から「```」までをテキスト ファイルにする
      • ファイル内行頭「```」を復元

の簡素なものにしています。

Python スクリプト: ファイルから Markdown へ
f2m.py
#!/usr/bin/env python

import os
import sys
import argparse

mode_map = {
    'applescript': ('.scpt',),
    'ada': ('.ada',),
    #'armasm': ('.s',),
    'awk': ('.awk',),
    'bash': ('.bash',),
    'bat': ('.bat',),
    'bpf': ('.bpf',),
    'cl': ('.cl',),
    'css': ('.css',),
    'csharp': ('.cs',),
    'cuda': ('.cu',),
    'c++': ('.c', '.cc', '.cpp', 'c++',
            '.h', '.hpp', 'h++'), # '.hh'
    'd': ('.d',),
    'diff': ('.diff',),
    'ecl': ('.ecl',),
    'elisp': ('.el',),
    'elm': ('.elm',),
    'email': ('.eml',),
    'eps': ('.eps',),
    'erl': ('.erl',),
    'erb': ('.erb',),
    'fea': ('.fea',),
    'ff': ('.ff',),
    'fortran': ('.f', '.f90', '.f95', '.f03', '.f15'),
    'fsharp': ('.fs',),
    'gd': ('.gd',),
    'glsl': ('.glsl', '.glslv', '.frag', '.vert'),
    'go': ('.go',),
    'groovy': ('.groovy', '.gvy', '.gy', '.gsh'),
    'haml': ('.haml',),
    'hack': ('.hack',), # '.hh'
    'hcl': ('.hcl',),
    'hlsl': ('.hlsl',),
    'hql': ('.hql',),
    'hs': ('.hs',),
    'html': ('.html',),
    'hx': ('.hx',),
    'hy': ('.hy',),
    'idlang': ('.idl',),
    'java': ('.java',),
    'js': ('.js',),
    'json': ('.json',),
    'ksh': ('.ksh',),
    'latex': ('.latex',),
    'lhs': ('.lhs',),
    'llvm': ('.ll',),
    'lua': ('.lua',),
    #'m68k': ('.s',),
    'make': ('.mak', '.mk'),
    #'mathematica': ('.m',),
    'matlab': ('.mat',),
    'md': ('.md',),
    'mf': ('.mf',),
    'minizinc': ('.mzn',),
    'mkd': ('.mkd',),
    #'ml': ('.ml',),
    'moon': ('.moon',),
    #'nasm': ('.s',),
    'nim': ('.nim',),
    #'objc': ('.m',), # '.h'
    'objcpp': ('.mm',), # '.hh'
    'ocaml': ('.mli',), # '.ml'
    'pascal': ('.p', 'pas', '.pp'),
    'patch': ('.patch',),
    'php': ('.php',),
    'pl': ('.pl',),
    'plist': ('.plist',),
    'ps': ('.ps',),
    'py': ('.py',),
    'pyx': ('.pyx',),
    'rb': ('.rb',),
    'rs': ('.rs',),
    'saas': ('.saas',),
    'sas': ('.sas',),
    'scala': ('.scala', '.sc'),
    'scss': ('.scss',),
    'sed': ('.sed',),
    'sh': ('.sh',),
    'sql': ('.sql',),
    'st': ('.st',),
    'swift': ('.swift',),
    'tcl': ('.tcl',),
    'tex': ('.tex',),
    'toml': ('.toml',),
    'ts': ('.ts',),
    'tsx': ('.tsx',),
    'vb': ('.vb',),
    'xml': ('.xml',),
    'yaml': ('.yaml', '.yml',),
    'yang': ('.yang',),
}

suffix_map = {}
for k in mode_map:
    for x in mode_map[k]:
        if x in suffix_map:
            raise Exception(x, suffix_map[x], k)
        suffix_map[x] = k

parser = argparse.ArgumentParser()
parser.add_argument('-o', '--output', default=None)
parser.add_argument('files', metavar='FILE', type=str, nargs='+')
args = parser.parse_args()

md = ''
for fn in args.files:
    fn = fn.replace('&', '&')
    fn = fn.replace(' ', ' ')

    ct = 'text'
    sfx = os.path.splitext(fn)[1]
    fnl = fn.lower()
    if fnl == 'makefile':
        ct = 'makefile'
    elif fnl == 'gnumakefile':
        ct = 'gnumake'
    elif sfx in suffix_map:
        ct = suffix_map[sfx.lower()]

    md = md + ('\n```%s:%s\n' % (ct, fn))
    for line in open(fn).readlines():
        if len(line) > 3 and line[:3] == '```':
            line = '\\' + line
        md = md + line
    md = md + '```\n'

o = args.output
if o is None or o == '-':
    o = sys.stdout
else:
    o = open(o, 'w')

o.write(md)

Python スクリプト: Markdown からファイルへ
m2f.py
#!/usr/bin/env python

import os
import sys
import argparse
import urllib.request

parser = argparse.ArgumentParser()
parser.add_argument('-c', '--console', action='store_true', default=False)
parser.add_argument('-m', '--math', action='store_true', default=False)
parser.add_argument('-o', '--outputdir', metavar='DIR', default=None)
parser.add_argument('markdown', metavar='URL/FILE', type=str, default=None)
args = parser.parse_args()

lines = []
if args.markdown is None:
    lines = sys.stdin.readlines()
elif args.markdown == '-':
    lines = sys.stdin.readlnes()
elif args.markdown.split(':')[0] in ('http', 'https'):
    with urllib.request.urlopen(args.markdown) as res:
        lines = []
        for line in res.read().decode().split('\n'):
            lines.append(line + '\n')
else:
    lines = open(args.markdown).readlines()

odir = args.outputdir
if odir is None:
    odir = '.'
if odir[-1] != '/':
    odir += '/'

exclude_modes = []
if not args.console:
    exclude_modes.append('console')
if not args.math:
    exclude_modes.append('math')

index = 0
output = None
for line in lines:
    if len(line) > 3 and line[:3] == '```':
        if output is None:
            code = line[3:].split(':', 1)
            mode = code[0].strip().lower()
            if len(mode) == 0:
                continue
            if mode in exclude_modes:
                continue
            fname = ''
            if len(code) > 1:
                fname = code[1].strip()
            if len(fname) == 0:
                fname = 'code:%d.%s' % (index, mode)
                index += 1
            fname = fname.replace(' ', ' ')
            fname = fname.replace('&', '&')
            path = odir + fname
            os.makedirs(os.path.dirname(path), exist_ok=True)
            print(path)
            output = open(path, 'w')
            continue

        output.close()
        output = None
        continue

    if output is None:
        continue

    if len(line) > 4 and line[:4] == '\\```':
        line = line[1:]
    output.write(line)

こちらの記事の取得例

$ python3 m2f.py -o sample https://qiita.com/ikiuo/items/2dc82e76337010f935a1.md
sample/main.swift
sample/zlibwrapper.m
sample/project-Bridging-Header.h
0
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
1