

Rubyのエラーコードについて質問です。  
「作りながら学ぶruby入門 第2版」という本で勉強しております。  
本書内ではRuby 1.9.3を使用していますが、私はRuby 3.0.5を使用しております。

本書内で蔵書管理アプリケーションを作成し、蔵書データを登録する際にコマンドプロンプトに日本語入力をするとエラーコードが出たので、解決策をご教授願えればと思います。(全て半角で入力するとエラーは出ないです)

蔵書データを登録する際に、書籍名や著者名に日本語があると以下のようなエラーが出てしまいます。(コマンドプロンプトの文字コードをutf-8設定にすると日本語入力した瞬間にコマンドプロンプトが強制終了されてしまいます)

Rubyおよびプログラム初心者なので、できるだけ具体的な解決方法をご提示いただければと思います。

**エラーメッセージ:**

[code]

    C:/Ruby30-x64/lib/ruby/gems/3.0.0/gems/dbi-0.4.5/lib/dbi/handles/database.rb:238:in `=~': invalid byte sequence in UTF-8 (ArgumentError)
            from C:/Ruby30-x64/lib/ruby/gems/3.0.0/gems/dbi-0.4.5/lib/dbi/handles/database.rb:238:in `!~'
            from C:/Ruby30-x64/lib/ruby/gems/3.0.0/gems/dbi-0.4.5/lib/dbi/handles/database.rb:238:in `check_statement'
            from C:/Ruby30-x64/lib/ruby/gems/3.0.0/gems/dbi-0.4.5/lib/dbi/handles/database.rb:233:in `sanity_check'
            from C:/Ruby30-x64/lib/ruby/gems/3.0.0/gems/dbi-0.4.5/lib/dbi/handles/database.rb:103:in `do'
            from ex2201.rb:90:in `addBookInfo'
            from ex2201.rb:152:in `run'
            from ex2201.rb:174:in `<main>'
    
[/code]

**ソースコード:**

[code]

    #encoding:utf-8
    require 'rubygems' #RubyGemsでインストールしたときには記述
    require 'dbi' #DBIを使う
    require 'date'
    
    class BookInfo
        # BookInfoクラスのインスタンスを初期化する
        def initialize(title, author, page, publish_date)
            @title = title
            @author = author
            @page = page
            @publish_date = publish_date
        end
    
        # 最初に検討する属性に対するアクセサを提供
        attr_accessor :title, :author, :page, :publish_date
    
        # BookInfoクラスのインスタンスの文字列表現を返す
        def to_s
            "#{@title}, #{@author}, #{@page}, #{@publish_date}"
        end
    
        #蔵書データを書式を付けて出力する操作を追加する
        #項目の区切り文字を引数に指定することができる
        #引数を省略した場合は改行を区切り文字にする
        def toFormattedString( sep = "\n")
            "書籍名: #{@title}#{sep}著者名: #{@author}#{sep}ページ数: #{@page}ページ#{sep}発刊日: #{@publish_date}#{sep}"
        end
    end
    
    # BookInfoManagerクラスを定義する
    class BookInfoManager
        def initialize(sqlite_name)
            #SQLiteデータベースファイルに接続
            @db_name = sqlite_name
            @dbh = DBI.connect("DBI:SQLite3:#{@db_name}")
        end
    
        #蔵書データベースを初期化する
        def initBookInfos
            puts "\n0.蔵書データベースの初期化"
            print "初期化しますか？(Y/yなら削除を実行します):"
            #読み込んだ文字を大文字に揃える
            yesno = gets.chomp.upcase
            if /^Y$/ =~ yesno
                #Yが一文字の時だけ初期化する
    
                #もしすでにこのデータベースにテーブル"bookinfos"があれば削除する
                @dbh.do("drop table if exists bookinfos")
                #新しく"bookinfos"テーブルを作成する
                @dbh.do("create table bookinfos(
                    id                   varchar(50)                   not null,
                    title                varchar(100)                  not null,
                    author               varchar(100)                  not null,
                    page                 int                           not null,
                    publish_date         datetime                      not null,
                    primary              key(id)
                );")
                puts "\nデータベースを初期化しました。"
                
            end
        end
    
        #蔵書データを登録する
        def addBookInfo
            puts "\n1.蔵書データの登録"
            print "蔵書データを登録します。"
    
            #蔵書データ1件分のインスタンスを作成する
            book_info = BookInfo.new("", "", 0, Date.new)
            #登録するデータを項目ごとに入力する
            print "\n"
            print "キー: "
            key = gets.chomp
            print "書籍名: "
            book_info.title = gets.chomp
            print "著者名: "
            book_info.author = gets.chomp
            print "ページ: "
            book_info.page = gets.chomp.to_i
            print "発刊年: "
            year = gets.chomp.to_i
            print "発刊月: "
            month = gets.chomp.to_i
            print "発刊日: "
            day = gets.chomp.to_i
            book_info.publish_date = Date.new(year, month, day)
    
            #作成した蔵書データを1件分をデータベースに登録する
            @dbh.do("insert into bookinfos values(
                \'#{key}\',
                \'#{book_info.title}\',
                \'#{book_info.author}\',
                \'#{book_info.page}\',
                \'#{book_info.publish_date}\'
            );")
    
            puts "\n登録しました。"
        end
    
        #蔵書データの一覧を表示する
        def listAllBookInfos
            #テーブル上の項目名を日本語に変えるハッシュテーブル
            item_name = {'id' => "キー", 'title' => "書籍名", 'author' => "著者名", 'page' => "ページ数", 'publish_date' =>"発刊日"}
    
            puts "\n2.蔵書データの表示"
            print "蔵書データを表示します。"
    
            puts "\n---------------"
    
            #テーブルからデータを読み込んで表示する
            sth = @dbh.execute("select * from bookinfos")
    
            #select文の実行結果を1行ずつrowに取り出し、繰り返し処理する
            counts = 0
            sth.each do |row|
                #rowは1件分のデータを保持しているので、
                #each_with_nameメソッドで値と項目名を取り出して表示する
                row.each_with_name do |val, name|
                    #項目名を日本の項目名にして表示する
                    puts "#{item_name[name]}: #{val.to_s}"
                end
                puts "-------------"
                counts += 1
            end
    
            #実行結果を解放する
            sth.finish
    
            puts "\n#{counts}件表示しました"
        end
    
        #処理の選択と選択後の処理を繰り返す
        def run
            while true
                #機能選択画面を表示する
                print "
                0.蔵書データベースの初期化
                1.蔵書データの登録
                2.蔵書データの表示
                9.終了
                番号を選んでください(0,1,2,9): "
    
                #文字の入力を待つ
                num = gets.chomp
                case
                when "0" == num
                    #蔵書データベースの初期化
                    initBookInfos
                when "1" == num
                    #蔵書データの登録
                    addBookInfo
                when "2" == num
                    #蔵書データの表示
                    listAllBookInfos
                when "9" == num
                    #データベースとの接続を終了
                    @dbh.disconnect
                    #アプリケーションの終了
                    puts "\n終了しました"
                    break;
                else
                    #処理待ち選択画面に戻る
                end
            end
        end
    end
    
    #ここからがアプリケーションを動かす本体
    #蔵書データのSQLite3のデータベースを指定している
    book_info_manager = BookInfoManager.new("bookinfo_sqlite.db")
    
    #BookInfoManagerの処理の選択と選択後の処理を繰り返す
    book_info_manager.run
    
[/code]


CC BY-SA 4.0
2023-02-08T16:11:57.947
93774
2023-08-23T00:08:16.370
2023-08-23T00:08:16.370
3060
57010
question
ruby windows コマンドプロンプト
コマンドプロンプトから Ruby の実行時に日本語入力を行うとエラーが発生する


Windows10以降に実装されたの新しいコンソール(Windows
Terminal含む)との組合せで発生するバグ(詳細は後述)と思われます。3.2.0以上では修正されていますので、Rubyのバージョンを3.2.0以上に上げてみてください。

なお、Windowsの新しいコンソール特有の問題であるため、WSLやレガシコンソール等を使用することで回避することも可能です(こちらも後述)。

* * *

#### バグの詳細

以前から、Windows10から実装された新しいコンソールやWindowsTerminalで、Windows版のRubyをコマンドプロンプトで使うと文字化けするという問題が報告されていました。しばらくよくわからないという話だったのですが、原因が判明し、解決策が判明しました。

[Bug #18588: ruby -e 'p gets' with japanese charactors gets additional invalid
leading chars and caught Encoding::InvalidByteSequenceError - Ruby master -
Ruby Issue Tracking System](https://bugs.ruby-lang.org/issues/18588)

原因としては、新しいコンソール自体にANSI版PeekConsoleInputでマルチバイト文字に対しておかしなデータを渡すバグがあり、Rubyが初めから間違ったバイト列を受け取っているため、文字コードが正しくないなどのエラーが発生したと言うことです。3.2.0から、上記を修正するコミット(UNICODE版PeekConsoleInputWを使うと言うもの)が採用されています。

#### 主な回避策

  * Rubyのバージョンを3.2.0以上にあげる。  
バージョンにこだわりが無ければ、最新に上げてしまうのが一番簡単かと思います。

  * レガシコンソールモードを使う。  
従来のコンソール(レガシコンソール)がなくなったわけでは無く、切り替えて使用することが可能です。詳しくは下記を参照してください。  
[レガシ コンソール モード – Windows デスクトップ - Windows Console | Microsoft
Learn](https://learn.microsoft.com/ja-jp/windows/console/legacymode)

  * WSLを使う。  
Rubyのメインの動作環境はLinuxであり、ライブラリを含めてLinuxの環境が最もテストされており、最も多く実行されています。そのため、最も安定して動作する(バグが少ない)のはLinuxになります。Windows10以降、WSLを使用することで簡単にLinux環境を構築できます。そのため、Rubyの開発などもWSLを使用した方が無難です。WSLのインストール方法は下記を参照してください。  
[WSL のインストール | Microsoft Learn](https://learn.microsoft.com/ja-
jp/windows/wsl/install)

  * Windowsでは無い環境を使用する。  
Windowsを捨てて、MacなりLinuxなり他のOSにしてしまえば、この問題は回避できます。


CC BY-SA 4.0
2023-02-11T04:00:10.717
93794
2023-02-11T05:42:52.773
2023-02-11T05:42:52.773
7347
7347
93774
answer