特定の位置にある改行を除去し,2行を1行にする(または,リストのふたつの要素をつなげてひとつにする)

たとえばこんなテキストがあるとする.

01 ID = 0001
02 ref = hoge
03 body = "hogehogehugahuga"
04 ID = 0002
05 ref = bura
06 body = "
07 buraburaguruguru"
...

06と07は本来1行なのだが,なぜか改行されてしまっている.
なんとなく構造のあるテキストだが,xmlのような標準的な仕様でもないので既存の方法でパースしたりはできない.
このとき,このテキストの構造は行同士の位置関係と行頭の文字列によって与えられているので,06行目のこの位置の改行は構造を正しくとれなくなる原因になる.

解決法

1行を1要素とするリストにすると,.pop()というメソッドが使える.

Pythonでリスト(配列)の要素を削除するclear, pop, remove, del | note.nkmk.me

# coding: utf-8

import codecs

filename = u'/hoge/huga.txt'

# テキストファイルを読み込み,一行ずつリストに入れる
with codecs.open(filename, 'r', 'utf-8') as fin:
    lines_list_raw = [line.rstrip(u'\r\n') for line in fin]

# 行が特定の形で終わっているとき,その次の行をとってきてつなげて,取り出した次の行をリストから消す
lines_list = [line + lines_list_raw.pop(num + 1) if line == u'body = "' else line for num, line in enumerate(lines_list_raw)]

解説

内包表記で書いている部分を通常のfor文で書くと以下.

lines_list = []
for num, line in enumerate(lines_list_raw):
    if line == u'body = ':
        line = line + lines_list_raw.pop(num + 1)
        lines_list.append(line)
    else:
        lines_list.append(line)

enumerate()は,リストのインデックスと値をタプルで返す関数.
これを使うとfor文で要素とインデックスを簡単に取り出せて便利.

.pop()はリストの組み込みメソッドで,指定したインデックスの要素を返し,リストからその要素を削除する.

雑感

別に特別なことでもない(.pop()は最近知ったが)ことしか書いていないが,よく使うものをどう使えば何ができるのかという工夫を蓄積するとコーディングが少しずつできるようになる気がする.特殊なモジュールを使いこなすとか課題自体が難しいようなことを訓練するとかはもっと先の話だと思う.

iPhoneのGoogleMapでナビゲーション中に音声が聞こえない問題

Settings -> Notifications -> GoogleMap の Sound を On にしたら声が出るようになった (iPhone6, iOS11.4).

Onになってるとバイブレーションがビービーうるさくて嫌だから切っていたのだが,画面を凝視しないと道がわからないのも嫌だ.アプリごとにバイブレーションだけ切る方法があればいいのにと思う.

OneDrive (for Business) の同期が進まない,または異様に遅い

  • OneDriveをアップデートしたら治った
  • アプリケーションのメニューにアップデートという選択肢がないが,インストールし直せばいいっぽい
  • 私はAppStoreからインストールしてたが,インストールしてあるのになぜかボタンが「インストール」になってた(普通は「開く」)

emacs shell の起動時に.bash_profileが読み込まれない問題

問題

emacsのシェルでpythonを実行すると,入っているはずのパッケージが呼び出されないことがある.

$ which python
/usr/bin/python
$ pyenv versions
  system (set by /Users/user/.pyenv/version)
  2.7.10
  3.4.6
∗ anaconda2-4.3.1
  anaconda3-4.3.1

こんな感じで,pyenvはanaconda2を呼び出しているつもりのようだが,shellが実行しているのはシステムのpythonになっている.

類似の問題

同じような問題をまとめてくれている人がいた.

emacs + elpy + pyenv-virtualenv でシステムの python が使われてしまう

elpyの詳細はここでは説明しない(できない)が,pythonIDE的な機能を提供してくれる拡張らしい.
とりあえず記述に従ってpyenv-virtualenvを入れ,elpyを入れ,あれこれやる.
このサイトも参考にした.

ちなみに

M-x package-install

を実行するには,

(package-initialize)
(setq package-archives
      '(("gnu" . "http://elpa.gnu.org/packages/")
        ("melpa" . "http://melpa.org/packages/")
        ("org" . "http://orgmode.org/elpa/")))

.emacsなりinit.elなりの先頭に書き込んでおく.

いくつか気づいたこと

まだ問題

とりあえずシェルでpyenvのpythonを読むことはできるようになったが,シェルを立ち上げてもその時点ではまだシステムのpythonを呼び出している.
あと,なぜか環境変数PS1が書き換えられていて,プロンプトが

(anaconda2-4.3.1) computer:~ user$ 

のようになっている.システムのpythonを呼び出しているのに.
.bash_profileに

export PS1="\h:\W \u$ "

のように書いているのだが,無視されているのか,どこかで自動的に上書きされているのかのどちらからしい.
emacs shellでは他の環境変数も半端に読み込まれている(なぜそんなことになるのか不明)が,ターミナルのshellだと他の変数は普通に読み込まれている.
ターミナルではpyenvも普通に読み込まれているので,pythonを切り替えるとプロンプトの表示も切り替わるが,やはりemacs shellはそうならない.

問題を整理すると,

  • emacs shellの起動時に.bash_profileを読み込んでいない
  • pyenvも正しく読み込めていない

やったこと

対症療法的に,shellの起動時に.bash_profileを読み込ませることにした.

.emacsに,

(defun open-shell-holizontally ()
  (interactive)
  (shell nil)
  (execute-extended-command nil "shell" "shell")
  (delete-window)
  (split-window-right nil)
  (other-window 1)
  (switch-to-buffer "*shell*" nil 'force-same-window)
  (insert "source ~/.bash_profile")
  (comint-send-input))

を書き込む.

もともと設定していた,バッファを縦に分割してshellを右側に出すマクロに付け加えた.

なおelispを直接書いたわけではなく,elmacroというパッケージを使ってキーボードマクロをそれっぽく変換しただけ.
elmacro.el : キーボードマクロで超簡単に自作コマンドを作ろう!

2019/5/9追記

こんな記事を見つけた.
teratail.com

とりあえず,export PYENV_VIRTUALENV_DISABLE_PROMPT=1を.bash_profileに書き込むという方法でうまくいっているように思う.

要素の個数を数える

ごくシンプルな例に整理する.

やりかた

lang_list = [u'perl', u'python', u'ruby', u'perl', u'python', u'python', u'ruby', u'python', u'ruby', u'lisp']

lang_dic = {}

for lang in lang_list:
    if lang not in lang_dic.keys():
        lang_dic[lang] = 1
    else:
        lang_dic[lang] += 1

# -> lang_dic = {u'perl': 2, u'python': 4, u'ruby': 3, u'lisp': 1}

やってること

数えるべき要素の集まりに対して,初めて見つけたら1と対応させ, 以降見つけるごとに1ずつ対応する数字を増やしていきたい.

要素と値を対応させるには辞書を使う方法と,リストやタプルの中に要素と値の組を入れ子にしたリストやタプルを入れる方法がある.
リストやタプルの入れ子だと値の書き換えがややこしく(タプルは書き換え不可),数えるときには辞書が便利.

lang_dic[lang] で要素に対応する値を呼び出し,+= 1 でその値に1を足している.
もしまだ辞書にその要素が入っていなかったら,lang_dic[lang] = 1で1と対応させて新規登録する.

ごく単純なひらがなカタカナ変換

毎回やるたびに忘れてて調べるのでメモ.

やりかた

hira = u'あ'
kata = unichr(ord(hira) - 96)
# -> kata = u'ア'

やってること

ord() は文字のコードを返す関数で,ascii型ならasciiコード,unicode型ならunicodeを返す.
unichr() は逆にコードからunicode型を返す.
ord() が返す値は実体参照?と呼ばれる値?で,ひらがなとカタカナは96ずつずれて同じように並んでいる.
ord() には文字を1文字ずつしか渡せないので,文字列はばらす必要があるし,文字列型はunicode型にしておく必要がある.

文字コードのことはよくわからないが,例えばこのサイトに載っている値はord()が返すのと同じものらしい.

追記

文字のコードというのは,文字に対応する数字のIDのようなもので,ascii型とかunicode型というのは文字のこと.
文字のコードは要は文字を数字で表したものなので,数字を足したり引いたりすると別の文字を表す数字が得られる.

ascii型というのは概ねアルファベットのこと,unicode型というのはたいていあらゆる文字を含んでいる.

文字列をばらす最も簡単な方法は,

code_list = []

strings = u"ほげほげふがふが"

for char in strings:
    code_list.append(ord(char))

# -> "ほげほげふがふが" の1文字ずつの文字コードがリストに入る

ffmpegで生成した音声ファイルの音が出ないときの対処法

私の経験では通常再生されないチャンネルに音声が放り込まれているケースが多い.

対処

$ ffmpeg -i input.mpeg -ac 1 out.wav

解説

  • ac 1でチャンネルを一つだけ生成する(から,優先的に再生されるチャンネルにデータが入る)
  • ac 2とすればステレオのままいけるかも(未確認)

チャンネルの問題が原因なのかどうか確認するには,Audacityhttps://www.audacityteam.org/で開くとわかる.波形のウィンドウが3つ以上表示されたら,おそらくチャンネルの問題.