ChatGPTでプログラミングしてみた

こんにちは。Tedです。

ChatGPTのプログラミング技術がどれくらいなのか、実際に使ってみました。 作成するのはテキストファイル変換プログラム。 Rubyを勉強がてら作りたいと思っても、なかなか作れてなかったプログラムです。

背景

私は15年以上前からPCで日記を付けてました。 単にテキストファイルに日付とその日にあったこと・思ったことをシンプルに書き留めてました。 テキストファイルは年単位として、年が変わったらあらたにファイルを作るようにしていました。

内容はこんな感じです。

8/3
特に外出するでもなく家で読書など。
子ども達はプールに行って元気。
庭で野球して遊んだり。
7/15以降、平日はなかなか手が出せてなかった書類の整理。

9/12
日本出張最終日。みんなで飲みに行く。
この一週間に取り組んだこと、その成果についてみんなが振り返りながら楽しそうに話す。

この日記、10年ほど前にDay Oneという日記アプリに移行しました。

Day One

Day One

  • Bloom Built Inc
  • ライフスタイル
  • 無料
apps.apple.com

このアプリは結構使い勝手がよく、今に至ります。

作るもの

Day Oneにはインポート機能があります。プレインテキストでのインポートも可能です。 そこで以前書き留めていた日記テキストファイルをDay Oneにインポートしたいと思っていました。 けれどなかなか実行に移せていなかったのは冒頭の通りです。 これをChatGPTくんに作ってもらおうと思います。

テキストファイルからインポートできると言っても、ルールに沿ったフォーマットでなければなりません。 Day Oneの仕様ではフォーマットは下記となります。

Date: YYYY年MM月DD日 hh:mm:ss JST

titleテキスト

本文

Date: YYYY年MM月DD日 hh:mm:ss JST

titleテキスト

本文

ChatGPTを使ってプログラミング

さっそく依頼

そこで、このような変換を行うRubyプログラムをChatGPTに依頼してみました。 使用したChatGPTモデルは GPT-4 です。

ChatGPTのチャットコンソールより、下記のようにタイプしました。

とあるテキストファイルがあります。 このテキストファイルは空行をセパレータとしたまとまりを持つテキストファイルです。 まとまりの先頭は A/B となっています。 この A は月を、 B は日を表します。

このファイルと、年を示す4桁の数字を与えると、以下のテキストファイルに変換するRubyプログラムを作成してください。 出力すべきテキストファイルの内容は以下の通りです。

  • A/B とある行は、 Date: YYYY年MM月DD日 22:00:00 JST とする。YYYYは与えられた年であり、MMはAでありDDはBである。
  • それ以外の行はそのまま出力する。
  • 空行は空行として出力する。

するとすぐに以下の回答が返ってきました。

以下に、指定された条件に従ってテキストファイルを変換するRubyプログラムを示します。このプログラムは、与えられた年とテキストファイルを読み込み、指定されたフォーマットに従って新しいテキストファイルに書き込みます。

require 'date'

def convert_text_file(input_file, output_file, year)
  File.open(input_file, 'r') do |file|
    File.open(output_file, 'w') do |new_file|
      file.each_line do |line|
        if line.strip.empty?
          new_file.puts
        elsif line =~ /^(\d{1,2})\/(\d{1,2})/
          month = $1
          day = $2
          new_date = DateTime.new(year.to_i, month.to_i, day.to_i, 22, 0, 0, '+09:00')
          new_file.puts "Date: #{new_date.strftime('%Y年%m月%d日 %H:%M:%S %Z')}"
        else
          new_file.puts line
        end
      end
    end
  end
end

input_file = 'input.txt'
output_file = 'output.txt'
year = 2023

convert_text_file(input_file, output_file, year)

このプログラムを使用するには、input.txt に変換する前のテキストファイルを配置し、year変数に年を指定して実行してください。この例では、yearに 2023 を指定しています。プログラムが実行されると、変換後のテキストファイルが output.txt として保存されます。

使い方まできちんと教えてくれるし、コードもきれいです。 変数名が適切に付けられているところもgoodです。

コマンドライン引数を使うように変更

ですが、これではプログラム内に入出力ファイル名などをハードコードしています。 書き留めたファイルは10以上あるので、コマンドライン引数で指定できるようにしたいと思いました。

そこでこのChatGPTとの会話を続けます。

上記のプログラムを、input_file, output_file, yearそれぞれコマンドラインから受け取るように変更してください。

するとさっそく回答が。

コマンドラインから input_file, output_file, year を受け取るように変更したRubyプログラムを以下に示します。

require 'date'
require 'optparse'

def convert_text_file(input_file, output_file, year)
  File.open(input_file, 'r') do |file|
    File.open(output_file, 'w') do |new_file|
      file.each_line do |line|
        if line.strip.empty?
          new_file.puts
        elsif line =~ /^(\d{1,2})\/(\d{1,2})/
          month = $1
          day = $2
          new_date = DateTime.new(year.to_i, month.to_i, day.to_i, 22, 0, 0, '+09:00')
          new_file.puts "Date: #{new_date.strftime('%Y年%m月%d日 %H:%M:%S %Z')}"
        else
          new_file.puts line
        end
      end
    end
  end
end

options = {}
OptionParser.new do |opts|
  opts.banner = "Usage: converter.rb [options]"

  opts.on('-i', '--input FILE', 'Input file') { |v| options[:input_file] = v }
  opts.on('-o', '--output FILE', 'Output file') { |v| options[:output_file] = v }
  opts.on('-y', '--year YEAR', 'Year') { |v| options[:year] = v }

  opts.on_tail('-h', '--help', 'Show this message') do
    puts opts
    exit
  end
end.parse!

if options[:input_file] && options[:output_file] && options[:year]
  convert_text_file(options[:input_file], options[:output_file], options[:year])
else
  puts "Error: Please provide input file, output file, and year."
  puts "Usage: converter.rb -i INPUT_FILE -o OUTPUT_FILE -y YEAR"
end

このプログラムは、コマンドライン引数で input_file, output_file, year を受け取ります。このプログラムを実行するには、以下のようなコマンドを使用してください。

ruby converter.rb -i input.txt -o output.txt -y 2023

このコマンドでは、input.txt を読み込み、output.txt に変換した結果を出力し、2023 を年として使用します。

コードはこのように表示される

補足説明も十分で、何から何までできる奴です。 利用開始数分で、すでにChatGPTは私の大切なパートナーになった感じです。

さっそく使ってみると問題が

早速コンバートしてみると、思わぬ結果が。 先のファイルを変換したら下記のようになりました。

Date: 2007年08月03日 22:00:00 +9:00
特に外出するでもなく家で読書など。
子ども達はプールに行って元気。
庭で野球して遊んだり。
Date: 2007年07月15日 22:00:00 +9:00以降、平日はなかなか手が出せてなかった書類の整理。

Date: 2007年09月12日 22:00:00 +9:00
日本出張最終日。みんなで飲みに行く。
この一週間に取り組んだこと、その成果についてみんなが振り返りながら楽しそうに話す。

5行目のように、日記本文中にある MM/DD までも変換されてしまいました。 これは私の指示がよくなかったようです。

変換すべきデータを詳しく指示

そこでさらに以下のようにChatGPTに指示しました。 人間相手なら「指示内容が不十分だったよ。ごめんね」などと言うところですが、ChatGPTにはシンプルに依頼内容だけを伝えます。

上記のプログラムでは、A/Bで始まる行も変換対象にしています。 変換するのはA/Bとなっている行のみとし、A/Bの後に文字がある行はそのまま出力するように変更してください。

すると下記のように更新したプログラムを出してきました。

require 'date'
require 'optparse'

def convert_text_file(input_file, output_file, year)
  File.open(input_file, 'r') do |file|
    File.open(output_file, 'w') do |new_file|
      file.each_line do |line|
        if line.strip.empty?
          new_file.puts
        elsif line =~ /^(\d{1,2})\/(\d{1,2})(\s|$)/
          month = $1
          day = $2
          new_date = DateTime.new(year.to_i, month.to_i, day.to_i, 22, 0, 0, '+09:00')
          new_file.puts "Date: #{new_date.strftime('%Y年%m月%d日 %H:%M:%S %Z')}"
        else
          new_file.puts line
        end
      end
    end
  end
end

options = {}
OptionParser.new do |opts|
  opts.banner = "Usage: converter.rb [options]"

  opts.on('-i', '--input FILE', 'Input file') { |v| options[:input_file] = v }
  opts.on('-o', '--output FILE', 'Output file') { |v| options[:output_file] = v }
  opts.on('-y', '--year YEAR', 'Year') { |v| options[:year] = v }

  opts.on_tail('-h', '--help', 'Show this message') do
    puts opts
    exit
  end
end.parse!

if options[:input_file] && options[:output_file] && options[:year]
  convert_text_file(options[:input_file], options[:output_file], options[:year])
else
  puts "Error: Please provide input file, output file, and year."
  puts "Usage: converter.rb -i INPUT_FILE -o OUTPUT_FILE -y YEAR"
end

これで変換結果は下記になりました。

Date: 2007年08月03日 22:00:00 +9:00
特に外出するでもなく家で読書など。
子ども達はプールに行って元気。
庭で野球して遊んだり。
7/15以降、平日はなかなか手が出せてなかった書類の整理。

Date: 2007年09月12日 22:00:00 +9:00
日本出張最終日。みんなで飲みに行く。
この一週間に取り組んだこと、その成果についてみんなが振り返りながら楽しそうに話す。

5行目の月日は変換されないようになりました。

さらに修正

このファイルをDay Oneにてインポートすると、正しく日時が取り込まれないことがわかりました。 どうもタイムゾーンの指定が +9:00 ではダメで、 JST としなければいけないようです。 それに、これはもともと私の指示でもそうしていたところです。 ChatGPTとしてはタイムゾーンの指定は JST でも +9:00 でも変わりないということなのでしょう。 たしかにそうではありますが、Day Oneが受け付けない以上は仕方がありません。

またこれは私の書き方のせいなのですが、タイトルとなる1行目に複数文を記述しているところがありました。 これだとその一行がそのままタイトルとなるため、タイトルが長くなりすぎてしまいます。 タイトル行に複数文が書かれている場合は2つめ以降は改行してもらうことにしました。

そこでこれらの修正をChatGPTに依頼します。

上記のプログラムにて、A/Bである行の出力の JST となるべき部分が +9:00 となっていますので修正してください。 また、A/Bである行の次の行の文中(文末ではない)に 。 がある場合、そこで改行するようにしてください。

すると以下のようにアップデートされました。 文句も言わずに黙々と修正に応じてくれるChatGPTくんはとても寛容です。

require 'date'
require 'optparse'

def convert_text_file(input_file, output_file, year)
  File.open(input_file, 'r') do |file|
    File.open(output_file, 'w') do |new_file|
      previous_line_was_date = false
      file.each_line do |line|
        if line.strip.empty?
          new_file.puts
          previous_line_was_date = false
        elsif line =~ /^(\d{1,2})\/(\d{1,2})(\s|$)/
          month = $1
          day = $2
          new_date = DateTime.new(year.to_i, month.to_i, day.to_i, 22, 0, 0, '+09:00')
          new_file.puts "Date: #{new_date.strftime('%Y年%m月%d日 %H:%M:%S')} JST"
          previous_line_was_date = true
        else
          if previous_line_was_date
            modified_line = line.gsub(/(?=[^])/) { |match| "#{match}\n" }
            new_file.puts modified_line
            previous_line_was_date = false
          else
            new_file.puts line
          end
        end
      end
    end
  end
end

options = {}
OptionParser.new do |opts|
  opts.banner = "Usage: converter.rb [options]"

  opts.on('-i', '--input FILE', 'Input file') { |v| options[:input_file] = v }
  opts.on('-o', '--output FILE', 'Output file') { |v| options[:output_file] = v }
  opts.on('-y', '--year YEAR', 'Year') { |v| options[:year] = v }

  opts.on_tail('-h', '--help', 'Show this message') do
    puts opts
    exit
  end
end.parse!

if options[:input_file] && options[:output_file] && options[:year]
  convert_text_file(options[:input_file], options[:output_file], options[:year])
else
  puts "Error: Please provide input file, output file, and year."
  puts "Usage: converter.rb -i INPUT_FILE -o OUTPUT_FILE -y YEAR"
end

このプログラムを実行すると、下記のように変換されました。

Date: 2007年08月03日 22:00:00 JST
特に外出するでもなく家で読書など。
子ども達はプールに行って元気。
庭で野球して遊んだり。
7/15以降、平日はなかなか手が出せてなかった書類の整理。

Date: 2007年09月12日 22:00:00 JST
日本出張最終日。
みんなで飲みに行く。
この一週間に取り組んだこと、その成果についてみんなが振り返りながら楽しそうに話す。

これでプログラムは一応の完成を見た感じです。

使用しているとエラーが

完成したプログラムを使って次々に変換していくと、突然とあるファイルを変換する際にエラーが表示されました。 そこで、このエラーがなぜ起こるのかChatGPTくんに聞いてみることにしました。

このプログラムを実行すると、下記のエラーが表示されました。何が原因と考えられますか?

ruby ./converter.rb -i Documents/Diaries/b2014.txt -o b2014.txt -y 2014
./converter.rb:9:in `strip': invalid byte sequence in UTF-8 (ArgumentError)
    from ./converter.rb:9:in `block (3 levels) in convert_text_file'
    from ./converter.rb:8:in `each_line'
    from ./converter.rb:8:in `block (2 levels) in convert_text_file'
    from ./converter.rb:6:in `open'
    from ./converter.rb:6:in `block in convert_text_file'
    from ./converter.rb:5:in `open'
    from ./converter.rb:5:in `convert_text_file'
    from ./converter.rb:47:in `<main>'

コンソールに出力されたエラーをそのままコピペして聞いてみたわけです。

すると下記のように回答してきました。

このエラーは、入力ファイルがUTF-8以外のエンコーディングで記述されているために発生しています。RubyはデフォルトでUTF-8エンコーディングを使用するため、異なるエンコーディングのファイルを読み込む際に問題が発生することがあります。

ファイルのエンコーディングを明示的に指定して対応する方法を以下に示します。
(以下略)

こんな風にエラーの原因を応えるだけでなく、ファイルのエンコーディング指定方法まで教えてくれました。

おかげでこれまでずっとできなかった過去の日記テキストをDay Oneにすべてインポートできました。

やってみて

ChatGPTでのプログラミングはとてもスムーズに進められます。 まるでプログラム言語に詳しい相棒と一緒にプログラミングしている感覚です。 この「相棒」はプログラミング以外にもきっと活躍してくれると思います。

それだけ有能な相棒ですから、今後ChatGPTに限らずAIを上手く使いこなすかどうかで仕事の生産性や日常が大きく変わると思います。 上手く使いこなすには、自分が言いたいこと・知りたいことを論理的に説明する力が必要になると思います。

これ、かなりオススメです。