質問者
「柹」という特殊文字をoracleに登録できません。

質問
-
「柹」という文字をoracleに登録しようとしています。
Oracleに問合せしたところ下記の対処にてsqldeveloperからは登録できるようになりました。
1.データベースの項目定義をNVARCHAR2にする。
2.SQLを UPDATE STAFFTBL SET name = N'柹’ where ・・のようにN'柹’と表記する。
3.sqldeveloperの「ツール」「プリファレンス」から環境の「エンコーディング」を"UTF-16LE"にする。
C#プログラム(Windowsアプリ)から同様にSQLを発行していますが、ORACLEに登録された文字
は「?」となり文字化けしています。
上記3のsqldeveloperにてUTF-16LEに設定した部分をどのようにc#でコーディングしたらいいか
わからず困っています。
初心者なので質問があいまいかもしれませんが、宜しくお願いいたします。
- 移動 星 睦美 2016年10月18日 1:50 Windows クライアント開発 から
すべての返信
-
接続に使用されているプロバイダーはODP.NETでしょうか?
ちなみに以下からインストールできます。Oracle Developer Tools for Visual Studio
http://www.oracle.com/technetwork/jp/developer-tools/visual-studio/overview/index.html★良い回答には回答済みマークを付けよう! MVP - .NET http://d.hatena.ne.jp/trapemiya/
-
こんにちは 返信有難うございます。
SqlDeveloperでSELECTした結果はただしく「柹」となっていますが、
SQL*PlusでSELECTした結果は「?」となっております。
また、クライアント端末(Windows7)のC#プログラムでSELECTした結果は「柹」となっています。
- 編集済み coffeetime0831 2016年10月18日 6:31
-
試してないので想像なのですが、おそらくクライアントPCにNLS_LANGを登録していると思います。
それをUTF-16LEに対応したものに変更すれば、SQL*Plusでもご自身のアプリでも正常に表示できるでしょうか。またはアプリ内でOracleConnectionインスタンスを生成した時に、
セッション・グローバリゼーション設定でキャラクタセットを変更してみてはどうでしょう。http://otndnld.oracle.co.jp/document/products/oracle10g/101/doc_v12/win.101/B15519-01/featGlobal.htm#i1008314
https://docs.oracle.com/cd/E16338_01/win.112/b66456/OracleGlobalizationClass.htm#i1009565- 編集済み Tak1waMVP, Moderator 2016年10月18日 6:22
-
加えてC#側の具体的なコードも挙げていただきたいです。
こんにちは、返信有難うございます。
オラクルとの接続部分は下記のとおりです。
class JSVDBAccess
{
public DataSet ReferTable(string strSQL)
{
OleDbConnection conn = new OleDbConnection();
OleDbCommand command = new OleDbCommand();
OleDbDataAdapter adapter = new OleDbDataAdapter();
DataSet ds = new DataSet();
conn.ConnectionString = @"Provider=OraOLEDB.Oracle;User ID=js;Password=xxxxxx;Data Source=XXX;";
command.Connection = conn;
command.CommandText = strSQL;
adapter.SelectCommand = command;
adapter.Fill(ds);
return ds;
}
public bool ModifyTable(string strSQL)
{
OleDbConnection conn = new OleDbConnection();
OleDbCommand command = new OleDbCommand();
try
{
conn.ConnectionString = @"Provider=OraOLEDB.Oracle;User ID=js;Password=xxxxx;Data Source=XXXXX;";
command.Connection = conn;
command.CommandText = strSQL;
conn.Open();
command.ExecuteNonQuery();
return true;
}
catch (Exception)
{
return false;
}
finally
{
conn.Close();
}
}
}- 編集済み coffeetime0831 2016年10月18日 6:35
-
trapemiyaさんも指摘されていますが.NETからOracleへの接続は
- ODP.NET
- .NET Framework Data Provider for Oracle(廃止済み)
- .NET Framework Data Provider for OLE DB + Oracle Provider for OLE DB
といくつかの方法があります。
挙げられたコードを見る限りOLE DBを使用されています。Oracle Provider for OLE DBのドキュメントを読む限りUnicodeモードと非Unicodeモードが存在するようで、行セットおよびパラメータのデータ型マッピングによると非Unicodeモードでは一旦Shift-JISに変換されるようです。しかしクライアント側から明示的にUnicodeモードで接続する方法はドキュメントからは見つけられませんでした。関係あるかわかりませんがOLEDB.NETデータ・プロバイダの互換性によると接続文字列に「OLEDB.NET=True」を追加すべきです。
なお、このような問題を回避するためにも余計な中間層を挟まないODP.NETを選択した方がいいかもしれません。
-
返信がおそくなりました。
ODP.NETに修正して実施しましたが、問題は解決しませんでした。
以下、修正したデータベースに接続するクラスです。
class JSVDBAccess
{
public DataSet ReferTable(string strSQL)
{
OracleConnection conn = new OracleConnection();
OracleCommand command = new OracleCommand();
OracleDataAdapter adapter = new OracleDataAdapter();
DataSet ds = new DataSet();
conn.ConnectionString = @"User ID=js;Password=*****;Data Source=JSVPDB;";
command.Connection = conn;
command.CommandText = strSQL;
adapter.SelectCommand = command;
adapter.Fill(ds);
return ds;
}
public bool ModifyTable(string strSQL)
{
OracleConnection conn = new OracleConnection();
OracleCommand command = new OracleCommand();
try
{
conn.ConnectionString = @"User ID=js;Password=*****;Data Source=JSVPDB;";
command.Connection = conn;
command.CommandText = strSQL;
conn.Open();
command.ExecuteNonQuery();
return true;
}
catch (Exception)
{
return false;
}
finally
{
conn.Close();
}
}
} -
クエリ(ModifyTable メソッドの strSQL)はどうなっているのですか?
見たところパラメータ化されてないですよね。SQL Server の場合ですが、以前以下のような話がありました。
nvarcharに日本語を入力すると?????????になる
https://social.msdn.microsoft.com/Forums/ja-JP/4de12921-4769-423e-9b39-ba36599fb3f0/nvarchar?forum=sqlazurejaパラメータ化すると問題はなくなるかもしれません。試してみてはいかがでしょう? (そもそも、パラメータ化クエリを使用するということは、セキュリティ対策として普通にやるべきことなのですが)
パラメータ化の副次的な効用
http://surferonwww.info/BlogEngine/post/2016/06/04/use-parameterized-query-to-avoid-unexpected-character-corruption.aspx
#今回の問題とは関係ない余計はお世話かもしれませんが、Exception をキャッチするのは止めた方がいいです。理由は以下の記事を見てください。NETの例外処理 Part.1
https://blogs.msdn.microsoft.com/nakama/2008/12/29/net-part-1/.NETの例外処理 Part.2
https://blogs.msdn.microsoft.com/nakama/2009/01/02/net-part-2/#.NET 4 からは破損状態例外は catch できなくなっているそうですが、「それでも Catch (Exception e) を使用するのはよくない」ということについては以下の記事を見てください。
破損状態例外を処理する
https://msdn.microsoft.com/ja-jp/magazine/dd419661.aspx
- 編集済み SurferOnWww 2016年10月24日 1:55 リンク貼り直し
-
「問題は解決しません」だけの記述では他者からみて現在の状況がまったくわかりません。(例えばフォントだけの問題かもしれません。)
とりあえずODP.NETへの移行ガイドに
Unicode -- This setting is unnecessary because ODP.NET is always Unicode aware
とありました。ODP.NETを使用していてUnicodeが正しく表示されないのだとしたら、ODP.NETもしくはデータベース、いずれにしてもOracle側の問題かと思います。
- 回答の候補に設定 星 睦美 2016年10月27日 1:30
-
> クエリは以下のとおりです。
'か' や 'き' は N プレフィックス無しでも文字化けしなくて、'柹' は N プレフィックスをつけても文字化けする(? になる)ということなのでしょうか?
'柹' は UTF-16 で 2 バイトで表せる Unicode の基本多言語面に属する文字で、その意味では 'か' や 'き' と同じです。違いは Shift_JIS に含まれるか否か('柹' は Shift_JIS には含まれない)ぐらいだと思うのですが・・・
パラメータ化は関係なさそうですね。Oracle 製品のことは Oracle のコミュニティで聞いたほうがいいかもしれません。
- 回答の候補に設定 星 睦美 2016年10月27日 1:30
-
実験してみましたが、文字列リテラル指定では、期待通りに渡すことができませんでした。
状況から判断すると、「柹」の文字が「'」に置換されてから渡されてしまっているように見えます。
(OS / Oracle / Oracle Client / ODP.NET Managed Driver のバージョンにもよるのかも?)とりあえずパラメーターで渡す分には成功したように見えます。
あるいは文字列埋め込みにするなら、RAW データ経由で渡すことはできるようで。using Oracle.ManagedDataAccess.Client; using System; using System.Diagnostics; using System.IO; namespace ConsoleApplication1 { public static class Program { static void Main() { File.Delete(Properties.Settings.Default.LogFile); using (var con = new OracleConnection()) using (var cmd = new OracleCommand()) { con.ConnectionString = Properties.Settings.Default.ConnectionString; con.Open(); cmd.Connection = con; cmd.CommandText = @" DECLARE CURSOR CSR IS SELECT TABLE_NAME FROM USER_TABLES WHERE TABLE_NAME = 'STAFFTBL'; BEGIN FOR REC IN CSR LOOP EXECUTE IMMEDIATE 'DROP TABLE ' || REC.TABLE_NAME || ' PURGE'; END LOOP; EXECUTE IMMEDIATE ' CREATE TABLE STAFFTBL ( PKEY NUMBER(5) PRIMARY KEY , NAME NVARCHAR2(40) ) '; END; "; // 下記を実行すると、STAFFTBL が再作成されるので注意 // cmd.ExecuteNonQuery(); // [OracleException] ORA-01756: 引用符付き文字列が正しく終了していません // cmd.CommandText = "INSERT INTO STAFFTBL VALUES ( 1220, N'\u67F9' )"; // cmd.ExecuteNonQuery(); // 登録はされるが、格納されるデータは「'」になる // DUMP(NAME, 1016) は 「Typ=1 Len=2 CharacterSet=AL16UTF16: 0,27」 cmd.CommandText = "INSERT INTO STAFFTBL VALUES ( 1222, N'\u67F9'' )"; cmd.ExecuteNonQuery(); // 登録はされるが、格納されるデータは「'」になる // DUMP(NAME, 1016) は 「Typ=1 Len=2 CharacterSet=AL16UTF16: 0,27」 cmd.CommandText = "INSERT INTO STAFFTBL VALUES ( 1224, N''\u67F9' )"; cmd.ExecuteNonQuery(); // [OracleException] ORA-01756: 引用符付き文字列が正しく終了していません // cmd.CommandText = "INSERT INTO STAFFTBL VALUES ( 1226, N''\u67F9'' )"; // cmd.ExecuteNonQuery(); // [OracleException] ORA-01756: 引用符付き文字列が正しく終了していません // cmd.CommandText = "INSERT INTO STAFFTBL VALUES ( 1240, '\u67F9' )"; // cmd.ExecuteNonQuery(); // 登録はされるが、格納されるデータは「'」になる // DUMP(NAME, 1016) は 「Typ=1 Len=2 CharacterSet=AL16UTF16: 0,27」 cmd.CommandText = "INSERT INTO STAFFTBL VALUES ( 1242, '\u67F9'' )"; cmd.ExecuteNonQuery(); // 登録はされるが、格納されるデータは「'」になる // DUMP(NAME, 1016) は 「Typ=1 Len=2 CharacterSet=AL16UTF16: 0,27」 cmd.CommandText = "INSERT INTO STAFFTBL VALUES ( 1244, ''\u67F9' )"; cmd.ExecuteNonQuery(); // [OracleException] ORA-01756: 引用符付き文字列が正しく終了していません // cmd.CommandText = "INSERT INTO STAFFTBL VALUES ( 1246, ''\u67F9'' )"; // cmd.ExecuteNonQuery(); // [OracleException] ORA-01756: 引用符付き文字列が正しく終了していません // cmd.CommandText = "INSERT INTO STAFFTBL VALUES ( 1420, N'柹')"; // cmd.ExecuteNonQuery(); // 登録はされるが、格納されるデータは「'」になる // DUMP(NAME, 1016) は 「Typ=1 Len=2 CharacterSet=AL16UTF16: 0,27」 cmd.CommandText = "INSERT INTO STAFFTBL VALUES ( 1422, N'柹'')"; cmd.ExecuteNonQuery(); // 登録はされるが、格納されるデータは「'」になる // DUMP(NAME, 1016) は 「Typ=1 Len=2 CharacterSet=AL16UTF16: 0,27」 cmd.CommandText = "INSERT INTO STAFFTBL VALUES ( 1424, N''柹')"; cmd.ExecuteNonQuery(); // [OracleException] ORA-01756: 引用符付き文字列が正しく終了していません // cmd.CommandText = "INSERT INTO STAFFTBL VALUES ( 1426, N''柹'')"; // cmd.ExecuteNonQuery(); // [OracleException] ORA-01756: 引用符付き文字列が正しく終了していません // cmd.CommandText = "INSERT INTO STAFFTBL VALUES ( 1440, '柹')"; // cmd.ExecuteNonQuery(); // 登録はされるが、格納されるデータは「'」になる // DUMP(NAME, 1016) は 「Typ=1 Len=2 CharacterSet=AL16UTF16: 0,27」 cmd.CommandText = "INSERT INTO STAFFTBL VALUES ( 1442, '柹'')"; cmd.ExecuteNonQuery(); // 登録はされるが、格納されるデータは「'」になる // DUMP(NAME, 1016) は 「Typ=1 Len=2 CharacterSet=AL16UTF16: 0,27」 cmd.CommandText = "INSERT INTO STAFFTBL VALUES ( 1444, ''柹')"; cmd.ExecuteNonQuery(); // [OracleException] ORA-01756: 引用符付き文字列が正しく終了していません // cmd.CommandText = "INSERT INTO STAFFTBL VALUES ( 1446, ''柹'')"; // cmd.ExecuteNonQuery(); // 登録はされるが、格納されるデータは「'」になる // DUMP(NAME, 1016) は 「Typ=1 Len=2 CharacterSet=AL16UTF16: 0,27」 cmd.CommandText = "INSERT INTO STAFFTBL VALUES ( 1600, N'柹柹')"; cmd.ExecuteNonQuery(); // 正常終了 // DUMP(NAME, 1016) は 「Typ=1 Len=2 CharacterSet=AL16UTF16: 67,f9」 cmd.CommandText = "INSERT INTO STAFFTBL VALUES " + " ( 2200, UTL_RAW.CAST_TO_NVARCHAR2(HEXTORAW('67F9')) )"; cmd.ExecuteNonQuery(); // 正常終了 // DUMP(NAME, 1016) は 「Typ=1 Len=2 CharacterSet=AL16UTF16: 67,f9」 cmd.CommandText = "INSERT INTO STAFFTBL VALUES ( :p1, :p2 )"; cmd.Parameters.Clear(); cmd.Parameters.Add("p1", OracleDbType.Decimal).Value = 3200; cmd.Parameters.Add("p2", OracleDbType.NVarchar2, 40).Value = "\u67F9"; cmd.ExecuteNonQuery(); cmd.Parameters.Clear(); // 正常終了 // DUMP(NAME, 1016) は 「Typ=1 Len=2 CharacterSet=AL16UTF16: 67,f9」 cmd.CommandText = "INSERT INTO STAFFTBL VALUES ( :p1, :p2 )"; cmd.Parameters.Clear(); cmd.Parameters.Add("p1", OracleDbType.Decimal).Value = 3400; cmd.Parameters.Add("p2", OracleDbType.NVarchar2, 40).Value = "柹"; cmd.ExecuteNonQuery(); cmd.CommandText = "SELECT PKEY, NAME FROM STAFFTBL ORDER BY PKEY"; using (var reader = cmd.ExecuteReader()) { while (reader.Read()) { File.AppendAllText(Properties.Settings.Default.LogFile, reader.GetInt32(0).ToString("D5") + "\t" + reader.GetString(1) + "\r\n"); } } con.Close(); } Process.Start("notepad.exe", Properties.Settings.Default.LogFile); } } }
- 編集済み 魔界の仮面弁士MVP 2016年10月26日 1:09