Blog(ブログ)
Posted on:
- Go 向けライブラリ
tcellansi紹介ページ。リポジトリ: https://github.com/noborus/tcellansi
- あいまい幅とは Unicodeには、幅が「あいまい(Ambiguous)」とされる文字が存在します。 ターミナル等の固定幅の世界では、アルファベット等の幅が1(半角)、漢字やひらがな等の幅が2(全角)になります。 あいまい幅とされている文字の多くは元々日本を含む東アジアの方で全角の文字で使われていた文字が多いです。 これらの一部の文字(記号)がUnicodeでは全角幅とはならずにAmbiguousとして定義されています。 Unicode文字の使用が増えている Unicodeが使用されるようになって、日本では当然ながらJISの文字コードが使用されていた文字がUnicodeになってからもそのまま使用されていました。 英語圏ではASCIIコードの範囲内の文字の使用が基本で、それ以外の文字はそれほど多くされていませんでした。 ただ最近では絵文字が世界中で使用されるようになっていて、ASCIIコードではなくUnicode(UTF-8)で解釈する必要があるテキストが増えてきました。 そういったテキストが増えてくるとターミナル上でもそれらの文字が使用されるようになり、絵文字以外の文字も使用されるようになってきています。 あいまい幅に該当する文字の使用も増えている CLIやTUIでも罫線(┌───┬───┐)を利用して境界を示すようなテーブル等の表示が増えてきました(以前は(+—+—+)のようなASCIIコードの範囲内の文字を使用していました)。 ところが罫線(┌─└─┬─┤)のような文字はあいまい幅の定義になっています。日本語環境でもこれらの文字を使用して枠が書かれていたので、それらの互換性のために全角で使用できる必要があったのですが、英語圏でも使用したいとなったら、幅が倍の全角よりも半角幅で枠が書けたほうが便利なため理にかなっています。 TUI,CLIでのあいまい幅の問題 しかし、ここに問題があります。 英語圏で想定している罫線は半角幅で表示されることを想定していますが、日本語環境では全角で使用されていたため全角を想定しています。 半角で想定していた罫線を文字数をそのままで全角の環境で表示すると横幅だけが倍になり、中の値の表示の幅はそのままなので枠だけがずれてしまいます。逆に全角幅を想定した文字数をそのまま半角で表示すると中の文字数(幅)に対して枠線は半分の幅を使用するため、中身だけずれてしまいます。 つまり枠線を書く場合も幅が1または2の両方ありえることを想定して、文字数ではなくその幅が確保できるように書かなければなりません。 そして、実際にその文字の幅が1になるのか2になるのかを教えてくれるような統一的なAPIがターミナルに存在していません。 アプリケーションによっては実際に書いてみて、読み取ることで判別するようなことをやっていることもあるようです。 単純に幅がわかったとしても枠を書くだけで最初の想定よりも大幅に大変になります。 例えば、TUIアプリケーションで罫線(┌ ─ ┘など)を使う場合、環境によっては1文字幅、別の環境では2文字幅で表示され、レイアウトが崩れることがあります。 GoのTUIで表示が崩れる場合も参考にしてください。 日本語環境であいまい幅=2を見直す必要がある TUIの罫線であいまい幅の文字が使われる場合は、両方の幅に対応するのが理想です。 しかし、罫線が使われるような場面では、そのハードルは非常に高いと感じています。 英語圏の作者では特に、この幅が変わることを想定するのが難しいです。 そのため、ターミナルエミュレーターの作者やTUIライブラリの作者は(東アジアの)言語環境での切り替えを止めて、あいまい幅の幅を1をデフォルトにして2に切り替える別の手段を用意したり、切り替えられないようになっていたりします。

psqlのPAGERを設定する PAGERをoff/onにする psqlでは pset値により、PAGERをon/off/alwaysにすることができます。alwaysにすると、psql起動時に出力が画面に収まっても常にPAGERが起動します。 オプションで一時的にPAGERをoffにするには以下のようにします(以降offの部分はonやalwaysが指定できる)。 psql -P pager=off .psqlrcファイルに設定すれば以後ずっとPAGERをoffにできます。 \pset pager off 起動後にPAGERをoffにする(psqlで接続後に実行する)には\psetコマンドを使用します。
\pset pager off \pset pagerを引数なしで実行するとトグルになり、実行するとon/offが切り替わります。
\pset pager Pager usage is off. # \pset pager Pager is used for long output. PostgreSQL 15.0からの追加機能 PostgreSQL 15.0からは、pager_min_linesが追加され、画面に収まらない場合に何行以上だったらPAGERを起動するか行数を指定できます。デフォルトは0で、画面に収まらない場合にPAGERを起動します(画面に収まる行数を指定しても無効になる)。 pager_min_linesよりもpset pager=alwaysの方が優先されます。 ターミナルのスクロール等でさかのぼれない場合のみPAGERを起動したい場合に便利です。

- 昔からUnix系のコマンドやCLIでは幅を揃えて出力することがあります。 ls, ps, df, 等々… このような出力はprintfのフォーマット指定により出力できます。 このフォーマット指定がわかれば、scanfにより読み取りが可能ですが、「出力内容だけ」から列を認識して読み取ることは簡単ではありません。 複数のスペースを区切り文字と見なす場合、出力を列に分割することは可能ですが、ヘッダーや値により不要に分割されてしまうことがあります。これを人間が読み取るのと同じように列の幅を推測するライブラリ/ツールを作りました。 guesswidth Goで作ってます。 完璧に読み取るのは難しいですが、スペース区切りの正規表現よりは、より良い結果が得られると思います。 読み取れるのは、主にヘッダー行があり、ヘッダーの列の幅が、その後に続く行の値の幅を表している場合です。 psやdfが当てはまります。lsはヘッダー行がないため、列の区切りは曖昧です。 psでは"PID"、“TTY”、“TIME”、“CMD"が下の値を表しているため、4つに分けることができます。 この例では複数スペースでも分割できますが、ヘッダーや値にスペースが含まれると正しく分けることができません。guesswidthではそのような形式にも対応します。 guesswidthの使い方はパイプ|で渡すだけです。デフォルトでは|を区切り文字として挿入します。 ps PID TTY TIME CMD 1145448 pts/2 00:00:00 zsh 1158532 pts/2 00:00:00 ps ↓ ps |guesswidth PID| TTY | TIME|CMD 1145448| pts/2 | 00:00:00|zsh 1158532| pts/2 | 00:00:00|ps 「,」を区切り文字を使用して CSV として区切ることもできます。CSVでは余分なスペースを取り除きます。

- PostgreSQLにおける列数0のSQL文とテーブルの扱いについて

- 手っ取り遅くSQLを学ぶ 「手っ取り遅くSQLを学ぶ」とは?すぐにSQLを使えるよう学ぶのではなく、まわりくどくSQLを理解して学んでいく方法で解説します。 「手っ取り早くSQLを学ぶ」と言ったら、よく使うSQLをなるべくシンプルな形にして、実際に試しながら、徐々に応用していく方法だと考えています。 「手っ取り遅くSQLを学ぶ」はその逆で、SQLの記法は後回しにして考え方を解説していきます。 まずはテーブル SQLは主にリレーショナル・データベース(RDBMS)で使用されるデータ操作のための言語です。 リレーショナル・データベースは「テーブル」をデータの集合として使用しているので、SQLを学ぶには、まずテーブルを理解する必要があります。 テーブルとは、訳すと「表」なので、コンピューターと関係なくても日常でも溢れている表現方法です。 名簿であったり、一覧表など一般に、よく目にします。 SQLで扱うテーブルは、表全般の中でもう少しルールが定まったものを対象とします。 他のプログラミング言語と同じく「型を持った値」があります。数値の123や文字列の’abc’等。 この「型を持った値」を複数持つことができます。これをレコードと言ったり、「行」と言ったりします。 このレコードが含んでいる値の数と型が一致している集合がテーブルです。 通常は、単純な数合わせではなく、型が一致しているだけでもなく、同じ意味を表しています。これをカラムと言ったり、「列」と言ったりします。 このカラムには名前を付けられることが多いです。「格納」する場合は、あとで取り出す必要があるので名前が必須になります。 この集合をレコードの配列と考えても良いかもしれませんが、この時点では順序は不動なことに注意が必要です。 他のプログラミングからは、2次元配列で表される場合もありますが、プログラミング言語を理解している人は、構造体の配列と考えた方が良いでしょう。実際にSQLで操作したデータをプログラミング言語で受け取るときには、構造体の配列に変換することがよくあります。 そして、テーブルの行も列も理論上は0以上無限まで可能です。 実際には上限はデータベースの仕様やコンピューターの限界値に左右されますが、下限は0から可能です。 行が0というのは、たとえば名簿リストを作成したとして該当者が存在しないことはあるのでよくある話だと思いますが、列が0というのは理論上存在しても現実にはないので、定義はできないようにしているデータベースもあります。 この「テーブル」単位にしておけば、自分の欲しいデータを取得できると考えて作られたのがリレーショナル・データベース(RDBMS)で、そのための記法がSQLです。 SQLとは? リレーショナル・データベースは、SQLを実行できるだけでなくテーブルを効率よく管理するためにいろいろな機能が備わっていますが、主要なSQLはテーブルを操作するため、少し切り離して考えることができます。 SQLはテーブル(複数でも可)から「1つの」テーブルを作ることを目的としています。 SQLをすでに知っている人からすると、テーブルを作ると言ったら「CREATE TABLE」と思うかもしれません。 データベースにテーブルを作るという狭い意味ではそうなのですが、その結果がテーブルになるという意味で広い意味では、テーブルを作るという目的であると言えます。 INSERT/UPDATE/DELETEなどの更新系SQL文は、実行した結果がデータベースに反映されます。 取得するSQL文と説明されることが多いSELECT文は、実際には取得ではなく、「SELECT」の意味の通り、「選択する」と解釈する方が正しいです。 「SELECT」文により選択された結果が指定されていない場合は、暗黙の了解として結果を取得します。 3つ以上のテーブルから1つのテーブルを「作る」場合、まず2つのテーブルから1つのテーブルを作成し、作成されたテーブルと残ったテーブルから、1つのテーブルを作成するといった風に考えることで3つ以上のテーブルから1つのテーブルを作成できます。 数学(というよりも算数)の「3+2+4」を「3+2」してその結果から「5+4」を計算するのと似ています。 つまりSQLはテーブルとテーブルの計算式と言えます。 JOIN テーブルの計算式として、一番基本は算数の足し算に相当するテーブルのJOINです。 ただし、テーブルとテーブルのJOINは、むしろ算数の掛け算に似ています。

- 「作ったツール紹介」というタイトルで発表しました https://pgunconf.connpass.com/event/240528/ 自分で作った以下のツールを紹介してます。 trdsql ov pgsp jpug-doc-tool また、ovの関連でページャーとして、lessとpspgも紹介してます。 とくに、lessはまだ正式リリース版ではないですが、ヘッダーオプションが追加されるということで、PostgreSQLに限らず全DBのCLIを使っている人にとって、朗報だと思います。 また、ヘッダー固定が可能なことを前提にすると他のアプリケーションの作り方も変わっていくものだと思っています。 jpug-doc-toolでみんなの自動翻訳@TexTraを使用できるようにしていることを紹介しました。 みんなの自動翻訳の説明はだいぶ省略しましたが、対訳語の登録や対訳集を学習させる等によりカスタマイズエンジンをみんなで育てることができれば、PostgreSQL向けの翻訳精度が上がっていくと思うので、利用者を増やしたいところです。

- PostgreSQLの日本語マニュアルのヘルパーツール PostgreSQLのマニュアルの日本語への翻訳に参加していますが、その翻訳に役立つツールとしてjpug-doc-toolを公開しました。 基本的な方針 PostgreSQL日本語マニュアルについては前に書いた手順と大きく変わっていませんが、SGMLの拡張子のままですがXMLとして解釈されるように変わっています。 XMLのライブラリを利用して読むことは可能になっていますが、PostgreSQLに入っているドキュメントは自動フォーマットされてレポジトリに入っていないため、XMLの処理系で処理して書き出すと元ドキュメントの比較が難しくなってしまいます。 そのため、XMLライブラリは使用せず、正規表現を使用し、必要なら最小限の書き換えをする作りになっています。 使い方 サブコマンドにより文書のチェックや抽出、書き換えができます。 $ jpug-doc-tool jpug-doc の翻訳を補助ツール。 前バージョンの翻訳を新しいバージョンに適用したり、 翻訳のチェックが可能です。 Usage: jpug-doc-tool [command] Available Commands: check 文書をチェックする completion Generate the autocompletion script for the specified shell extract 英語と日本語翻訳を抽出する help Help about any command list 辞書から英語と日本語訳のリストを出力する mt APIを使用して文字列を翻訳する replace 英語のパラグラフを「<!–英語–>日本語翻訳」に置き換える Flags: –config string config file (default is $HOME/.jpug-doc-tool.yaml) -h, –help help for jpug-doc-tool -t, –toggle Help message for toggle Use "jpug-doc-tool [command] –help" for more information about a command. jpug-doc-toolはpostgresql/doc/src/sgml/で実行します。拡張子sgmlのファイルを指定するとそのファイルを対象にします。指定しなかった場合はドキュメントを全て対象とします。

- mdtsql v0.0.3をリリースしました mdtsql v0.0.3をリリースしました。 Markdown テーブルに対してSQLを実行できるツールです。 README.mdやGitHubのWikiなどで、Markdownのテーブルを書くことがありますが、ドキュメント翻訳担当リスト14.0のようにテーブルが大きくなる場合に手で編集するのも大変ですが、探し出したり、集計したり、更新したり、といった作業が面倒になることがあります。 そこでtrdsqlのモジュールを使って、Markdown Tableに対してもSQLを実行できるようにしました。 trdsqlは既に様々なフォーマットに対して実行できるようになっているため、Markdownを追加しても良いわけですが、一緒にするにはMarkdown用のオプションが必要になるのもどうかと思って、別にしてあります。 mdtsqlはMarkdownファイルに対して実行すると解析してテーブルがあればテーブル情報を表示します。markdownにテーブルは複数含むことができるため、Markdown内のテーブルを指定するテーブル名をここで得ます。 「ドキュメント翻訳担当リスト13.1.md」というファイルに実行してみます。 $ mdtsql ドキュメント翻訳担当リスト13.1.md Table Name: [ドキュメント翻訳担当リスト13.1] +————-+——+ | column name | type | +————-+——+ | ファイル名 | text | | 担当者 | text | | 進捗 | text | | 備考 | text | +————-+——+ Table Name: [ドキュメント翻訳担当リスト13.1_2] +————————+——+ | column name | type | +————————+——+ | お名前 | text | | マニュアルへの表記 | text | | マニュアルへの記載可否 | text | | 主な貢献内容 | text | +————————+——+ テーブル名がわかったら、-q SQL文でSQLを実行できます。

- pgsp は PostgreSQLの pg_stat_progress viewを監視して表示するCLIツールです。 pg_stat_progress PostgreSQLでは、いくつか時間がかかる処理に対して進捗状況が見られるViewがあります。 Viewの名前は pg_stat_progressではじまり、analyze, cluster, create_index, vacuum, basebackup (version 14からは copyが追加される予定)などが取得できます。 詳しくは progress-reportingを参照してください。 これらのViewは処理が始まったときにレコードが追加されて、変化する処理状況(フェーズや処理した数)を更新していき、終了するとレコードが消えます。SQLで簡単に確認できるので便利ですが、常に更新されていくため、状況を逐一見たいときにはpsqlでは\watch等を利用してSELECTを繰り返して見る必要があります。 SELECT * FROM pg_stat_progress_analyze; pgsp pgspはこれらのViewを監視して表示する専用のCLIツールです。Go製です。 やっていることはシンプルでpg_stat_progress_* のViewを定期的にSELECTで取得し、レコードが追加、更新されたら進捗に相当する数値でプログレスバーを更新します。 PostgreSQLに普通に接続にいくので接続情報(ホスト、ポート、ユーザー、パスワード等)が必要です。 –dsnで設定してください。 pgsp –dsn 'host=ホスト名 port=ポート番号 user=ユーザー名 password=パスワード' UNIXドメインソケットを使用する場合はhostにpathを書きます。 pgsp –dsn 'host=/var/run/postgresql' 設定ファイルとして $HOME/.pgsp.yaml に書くこともできます。

- 概要 goのTUIについての2020年の最終更新版です。 goでTUI(text user interface)を作成する場合にライブラリを使用するのが一般的です。 goのTUIライブラリはだいたい以下に分類されます。 termbox-go系 tcell系 bubbletea系 その他 goのTUIライブラリはtermbox-go系、tcell系の利用が多かったですが、彗星のごとくbubbleteaが登場しました。 bubbleteaはThe Elm Architectureに基づいて作られているというフレームワークで、追加のコンポーネントとしてbubblesもあり、もう一つの系統として選択肢になると思います。 TUIライブラリを謳っている場合は、だいたい上記3つを元に実装されている場合が多いです。 TUIはエスケープシーケンスを使用すれば、ライブラリを使用しなくても実現できますが、端末によりエスケープシーケンスが変わっていたりするので、マルチプラットフォームで動作するのは難しくなります。 そのため、独自に一から作成するよりは、これらのライブラリの上に便利な機能を足す形になります。 termbox-go系 termbox-goは、老舗で現在も多く使われていますが、開発は停滞傾向で、termbox-goにもそれほど保守しない方向だと書かれています。 termbox-goを使用して、より高度なウィジットを実装したライブラリにgocuiがあります。 termbox-go gocui termui termbox-goのimported by tcell系 tcellは、termbox-goよりも新しくtermbox-goを意識して開発され、今も開発も続いています。 tcellは基本的な機能しか提供しませんが、tcell/viewsには、少し高度なウィジットがあります。 また、より高度なウィジットを実装したライブラリとしてtviewがあり、よく使用されています。また、そこからForkしたcviewも候補に入れておくと良いかも知れません。 さらに元々termbox-goを使用していたgocuiをtcellに変更したawesome-gocui/gocuiも開発されています。 tcell tcell/views tview cview cbind gowid goban awesome-gocui/gocui tcellのimported by bubbletea系 端末全部を使用するモードしかないtermbox-goとtcellと違い彗星のごとく現われたbubbleteaは現在のプロンプトから対話するような、ちょっとしたプログラムから端末全部を使用するTUIまでサポートしています。

- きっかけ tom__boさんが書かれた8.0.22でのprepared statementの挙動変化 で、ORDER BY に列番号を指定する問題に注目が集まりました。 その中で紹介されていた、 細部 For a prepared statement of the form SELECT expr1, expr2, … FROM table ORDER BY ?, passing an integer value N for the parameter no longer causes ordering of the results by the Nth expression in the select list; the results are no longer ordered, as is expected with ORDER BY constant.

- cbindとは? cbindはtcellのキーイベントとイベントハンドラを結びつけるライブラリです。 tcellのキーイベント tcell ではキー入力がイベントの1つとして取得できます。 tviewでもtcellのイベントを使用しているので、同じ様にイベントとして取得します。 tcellのキーイベントを取得するのは以下のようにswitch caseでキーを判別して、イベントハンドラを呼び出すのが一般的です。 ev := screen.PollEvent() switch ev := ev.(type) { case *tcell.EventKey: switch ev.Key() { case tcell.KeyEscape: close(quit) return } case tcell.KeyEnter: action() return } ここのtcell.KeyEscapeは constの数値として定義されています(キーボードに存在する英数字などの文字はruneで入ってきます)。 キー割り当てが少ないうちは、このまま追加していけば機能を増やせるので分かりやすいですが、キー割り当てが多くなってくると以下のような問題が出てきます。 修飾キー(CTRL、ALT…)が押された場合に動作が変わる場合はさらに分岐する キー割り当てをドキュメント化するのが大変になる キー割り当てのヘルプが必要になる キー割り当てを人によって変更したくなる ドキュメント化やヘルプはコードで実装した後、手間を掛けて書いていけばなんとか解決できますが、キー割り当ての変更に対応するには、元のままのコードでは不可能です。 cbindを使用 そこで使用したいのがcbindです。 cbind は Set()でキーの文字列といイベントハンドラを結びつけて登録できます。 実際にキーイベントが起きたら、cbindに任せれば登録されていたイベントハンドラが実行されることになります。

- これまで goのTUIについて tcellについて イベント tcellのイベントは、NewScreen()で作成したスクリーンのPollEvent()で取得できます。 その名の通り、イベントが起こるまでポーリング(polling)して待つので、起こらない限り止まったままになります。 PollEvent()でイベントが起こったらイベントに応じて処理し、SetContent()でセットし、次のイベントが起こる前にDraw()で描画する。 というのが、実際のメインルーチンになります。 このメインルーチンをgoroutineで動かし、終了のイベントがきたらchannelに通知して通知を受信したらFini()を実行して終了するのが一般的な流れです。 キーイベント イベントの中でも重要でよく使用するのがキーイベントです。 以下のプログラムは左上に打ったキーが表示されます。ESCキー又はEnterキーで終了します。 package main import ( "log" "time" "github.com/gdamore/tcell" ) func main() { screen, err := tcell.NewScreen() if err != nil { log.Fatal(err) } if err = screen.Init(); err != nil { log.Fatal(err) } defer screen.Fini() screen.SetContent(0, 0, '_', nil, tcell.StyleDefault) quit := make(chan struct{}) go func() { for { screen.Show() ev := screen.PollEvent() switch ev := ev.(type) { case *tcell.EventKey: switch ev.Key() { case tcell.KeyEscape, tcell.KeyEnter: close(quit) return case tcell.KeyRune: screen.SetContent(0, 0, ev.Rune(), nil, tcell.StyleDefault) time.Sleep(time.Millisecond * 100) } } } }() <-quit }

- SetContent() goのTUIについてで書いたようにtcellのSetContent()は1文字設置していくのでASCIIの範囲内だと簡単ですが、Unicodeの世界では注意すべき点があります。 まず日本語などの全角幅の文字と半角幅の文字が混在すると全角幅のときには、次の文字は1つとばして設置するといったことが必要になります。 単純に実装する場合はrunewidth.RuneWidth()を使用すれば、runeの文字幅を0,1,2で返してくれるので、その分xをずらせば表示されます。以下が実装例です。文字列を渡せるsetContents()で処理しています。 package main import ( "log" "github.com/gdamore/tcell" "github.com/mattn/go-runewidth" ) func setContents(screen tcell.Screen, x int, y int, str string, style tcell.Style) { for _, r := range str { screen.SetContent(x, y, r, nil, style) x += runewidth.RuneWidth(r) } } func main() { screen, err := tcell.NewScreen() if err != nil { log.Fatal(err) } if err = screen.Init(); err != nil { log.Fatal(err) } defer screen.Fini() setContents(screen, 0, 10, "あいうえお", tcell.StyleDefault) screen.Show() quit := make(chan struct{}) go func() { for { ev := screen.PollEvent() switch ev.(type) { case *tcell.EventKey: close(quit) } } }() <-quit } 上記では結合文字は無視されて表示されます。結合文字を出したい場合は、文字列をループしている中でruneが結合文字のコード範囲だった場合には、前の文字のcombcに入れた上でSetContent()を実行する必要があります。

- 概要 ライブラリの状況を鑑みてgoのTUIについて2020年最終版に更新しました。 goでTUI(text user interface)を作成する場合にライブラリを使用するのが一般的です。 goのTUIライブラリはだいたい以下に分類されます。 termbox-go系 tcell系 その他 TUIライブラリを謳っている場合は、だいたい上記2つを元に実装されている場合が多いです。 TUIはエスケープシーケンスを使用すれば、ライブラリを使用しなくても実現できますが、端末によりエスケープシーケンスが変わっていたりするので、マルチプラットフォームで動作するのは難しくなります。 そのため、独自に一から作成するよりは、これらのライブラリの上に便利な機能を足す形になります。 termbox-go系 termbox-goは、老舗で現在も多く使われていますが、開発は停滞傾向で、termbox-goにもそれほど保守しない方向だと書かれています。 termbox-goを使用して、より高度なウィジットを実装したライブラリにgocuiがあります。 termbox-go gocui termui termbox-goのimported by tcell系 tcellは、termbox-goよりも新しくtermbox-goを意識して開発され、今も開発も続いています。 tcellは基本的な機能しか提供しませんが、tcell/viewsには、少し高度なウィジットがあります。 また、より高度なウィジットを実装したライブラリとしてtviewがあり、よく使用されています。 tcell tcell/views tview gowid goban tcellのimported by その他 termbox-goとtcellはいずれも端末画面をまるまる使用することを前提に作られています。起動すると現在の端末画面は消えて(終了時に戻すことは可能)、新しい画面が表示されます。

- 結論 gnome-terminalを使用している場合は、設定の「曖昧幅の文字(W)」と環境変数RUNEWIDTH_EASTASIANを一致させよう。 Ambiguous width(曖昧幅) ターミナル上のアプリケーション(TUI)では、GUIと違って文字単位で描画されます。 そして1文字の幅は固定されていて、アルファベットは1文字分とすると日本語などは2文字分使用する、いわゆる半角全角の世界です。 ただし、既にUnicode(UTF-8)が標準となっているので、バイト数と文字幅は関係しないようになっています。 Unicodeでは幅が決まっている文字がほとんどですが、一部に「Ambiguous; 曖昧」とされている文字があります。 以前は英語圏のアプリケーションではASCIIの範囲内のみを使用していて「Ambiguous」な範囲の文字を使用するのは、それ以外の地域の人だったため、全角幅で問題になることは無かったのですが、Unicodeの使用が拡大するにつれて英語圏の方が作るアプリケーションでも「Ambiguous」な幅の文字が使用されることが増えてきました。 特にTUIアプリケーションでは、罫線「┌ ├ ─ ┘等」を使用して枠線を表現することがあります。これが「Ambiguous」な幅として、英語圏では1文字幅で表示できる様になっているため、2文字幅と解釈して表示しようとすると表示が崩れてしまいます。 ターミナル上で罫線を使用したプログラムがズレる場合はこれが原因です。 そのための対応として、gnome-terminalではPreferencesから「曖昧幅の文字(W)」を半角/全角で変更出来るようになっています。 これを半角にすれば、罫線が1文字幅で表示されるため、表示が直ります。 例えば、psqlの \pset linestyle unicodeをgnome-terminalで使用するには、ここを半角にしておかないと縦の線が揃わなくなります。 ただし、これはアプリケーションが幅を半角幅と仮定しているのに合わせているだけなので、別のアプリケーションでは合わなくなるといったことが起こります。 go-runewidth のAmbiguous width go言語では、1文字がターミナルで半角なのか全角なのかを判断するには、go-runewidthで判断するのがデファクトスタンダードになっていると思います。

- 前提 LOAD DATA INFILEはMySQLサーバーがファイルを読み取ってデータベースのテーブルにインポートする構文ですが、LOAD DATA LOCAL INFILEはクライアント側のファイル(の内容)をサーバー側に送信してインポートします。 このLOCAL指定ですが、セキュリティ上の問題を抱えているため、最近のバージョンだとデフォルトで使用できない設定に変更されたりしています。 そもそも LOAD DATA LOCAL INFILE の仕組みは、MySQLのLOAD DATA構文を(クライアント側ではパースして解釈しないので)サーバー側に送ってLOCAL INFILEの場合ファイル名をクライアントに伝えて、クライアントがそのファイル(の中身)をサーバー側に送信するようになっています。 サーバー側からLOAD DATA LOCAL INFILEに書いてあったファイル名とは違うファイル名を伝えられてもそのファイルを送信してしまう可能性があるため、セキュリティのリスクがあります。 LOAD DATA LOCAL INFILE(go) goのmysqlドライバでは、LOAD DATA LOCAL INFILE supportにあるように mysql.RegisterLocalFile(filepath)やmysql.RegisterReaderHandler(name, handler)という関数が追加されていてセキュリティ上の問題を解決するような拡張がされています。 mysql.RegisterLocalFile(filepath)は、LOAD DATA LOCAL INFILEを実行する前にあらかじめ送信するファイル名を登録しておいて、登録してあるファイルのみを送信することでリスクを軽減しています。 mysql.RegisterLocalFile("/tmp/test.csv") db.Exec("LOAD DATA LOCAL INFILE '/tmp/test.csv' INTO TABLE test") また、mysql.RegisterReaderHandler(name, handler)では、あらかじめクライアントプログラム側がファイルを開く等してできたio.Readerインターフェイスを<name>と共に登録しておき、LOAD DATA LOCAL INFILE ‘Reader::<name>’ INTO TABLE テーブル名 によりio.ReaderからReadしてサーバー側に送信します。
