[topic] 文字コードの変換

1.shift-jisで書かれた文字列をeuc-jpの文字列に変換して返す関数を作ってください。

2.shift-jisで書かれたファイルをeuc-jpに変換してファイル出力するしてください。ただし1で作成した関数を利用せずに。

何気にどう書く?orgに文字コード系の話が出てなかったような気がしたので投稿してみる。

2は言語仕様レベルでさくっといける場合(perlのencodingとか)でお願いします。
読み込みや書き込みのレベルで、文字コードを考えないでスパッといけるのが望ましい。

Posted feedbacks - Nested

Flatten Hidden
Squeak Smalltalk で。

Squeak3.8 以降に組み込みの多言語化機構では、TextConverter のサブクラスに定義された各種文字コード向けコンバータクラスのインスタンスを介して相互に変換できます。

ファイル入出力については、読み書きに用いるファイルストリームの converter 属性にあらかじめ適切な文字コード向けのコンバータを指定しておきます。
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
| sjisToEucStr result |
sjisToEucStr := [:sjisStr |
    | str |
    str := sjisStr convertFromEncoding: #sjis.
    str convertToEncoding: #eucjp].

result := sjisToEucStr value: ('あいう' convertToEncoding: #sjis).
^result asByteArray   "=> a ByteArray(164 162 164 164 164 166) "


| sjisToEucFileNamed result line |
sjisToEucFileNamed := [:in :out |
    in := FileStream oldFileNamed: 'in.sjis'.
    in converter: (TextConverter newForEncoding: #sjis).
    out := FileStream newFileNamed: 'out.euc'.
    out converter: (TextConverter newForEncoding: #eucjp).
    [(line := in nextLine) notNil] whileTrue: [out nextPutAll: line; cr].
    in close.  out close].

sjisToEucFileNamed value: 'in.sjis' value: 'out.euc'.
result := FileStream oldFileNamed: 'out.euc'.
result binary.
^result contentsOfEntireFile   "=> a ByteArray(164 162 164 164 164 166 13) "

C# というか、 .NET 2.0 で。エラー処理は省きました。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
using System;
using System.IO;
using System.Text;

class Program {
    static byte[] SjisToEuc(byte[] bytes) {
        return Encoding.Convert(Encoding.GetEncoding("shift-jis"), Encoding.GetEncoding("euc-jp"), bytes);
    }
    static void ConvertFile(string path_in, string path_out) {
        using (StreamReader sr = new StreamReader(path_in, Encoding.GetEncoding("shift-jis")))
        using (StreamWriter sw = new StreamWriter(path_out, false, Encoding.GetEncoding("euc-jp"))) {
            while (!sr.EndOfStream) sw.Write((char)sr.Read());            
        }
    }
    static void Main(string[] args) {
        // buffは、Shift-JISで "日本語文字列"
        byte[] buff = new byte[] { 0x93, 0xFA, 0x96, 0x7B, 0x8C, 0xEA, 0x95, 0xB6, 0x8E, 0x9A, 0x97, 0xF1 };
        byte[] result = SjisToEuc(buff);

        Console.WriteLine(BitConverter.ToString(result));
        // output : C6-FC-CB-DC-B8-EC-CA-B8-BB-FA-CE-F3
        
        ConvertFile(@"D:\sjis.txt", @"D:\euc.txt");
    }
}
Gaucheでは、「shift jisで書かれた文字列」とか「eucjpで書かれた文字列」という言い方はちょっと微妙です。エンコーディングは原則として処理系の「外部」にあるもので、言語レベルでの文字列は内部エンコーディングで統一されているので(入出力時や外部ライブラリ呼び出し時に変換)。ただ、文字列を無理やりバイト列として解釈させることが(今は)できるので、それを使うと一応「shift jisで書かれた文字列」みたいなものを扱うことはできます。#*"..." というのが「生の」文字列の表記です。

gosh> (ces-convert "いろは" 'utf-8 'sjis)
#*"\x82\xa2\x82\xeb\x82\xcd"
gosh> (sjis2eucjp #*"\x82\xa2\x82\xeb\x82\xcd")
#*"\xa4\xa4\xa4\xed\xa4\xcf"
gosh> (ces-convert #*"\xa4\xa4\xa4\xed\xa4\xcf" 'eucjp 'utf-8)
"いろは"

ただ、こういう扱いは意味的にすっきりしないので、将来はバイトベクタとして扱ってもらうようにするかもしれません。
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
(use gauche.charconv)

;; 1
(define (sjis2eucjp str)
  (ces-convert str 'sjis 'eucjp))

;; 2
(define (sjis2eucjp-file infile outfile)
  (call-with-input-file infile
    (lambda (in)
      (call-with-output-file outfile
        (lambda (out) (copy-port in out))
        :encoding 'eucjp))
    :encoding 'sjis))

よく知らなかったので調べながら……

文字列の変換は shiro さんの #4628 と同様にバイト列の変換にしています。ファイル入出力の際は :external-format を指定することで文字コード指定ができます。

いずれも処理系依存です。CLISP 2.38 で動作確認しました。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
;;; 1
(defun sjis-to-euc-jp (bytes)
  (convert-string-to-bytes
   (convert-string-from-bytes bytes charset:shift-jis)
   charset:euc-jp))

;;; 2
(defun sjis-to-euc-jp-file (from to)
  (with-open-file (in from :direction :input
                     :external-format charset:shift-jis)
    (with-open-file (out to :direction :output
                         :external-format charset:euc-jp)
      (do () ((null (listen in)))
        (write-line (read-line in) out)))))

Javaの文字列にはエンコーディングの概念がありません(内部的にはすべてUnicode)。

そこで、「Shift_JISのバイト列をEUC-JPのバイト列に変換する」と解釈しました。

せっかくなので、処理系による違いを知りたいです。

私が試したJava 1.6では、Shift_JISの円記号(0x5C)は内部的にバックスラッシュ(U+005C)になるようですが、これは規格通りではありません。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
import java.io.*;

public class Chars {
    public static void main(String[] args) {
        try {
            String s="こんにちは\\";
            for(byte b:sjis2eucjp(s.getBytes("SJIS"))) System.out.printf("%x ",b);
            
            file(new File("d:\\sjis.txt"),new File("d:\\eucjp.txt"));
        } catch (Exception e) {e.printStackTrace();}
    }
    
    //Shift_JISのバイト列をEUC-JPのバイト列に変換して返す関数
    static public byte[] sjis2eucjp(byte[] sjis) throws Exception{
        return (new String(sjis,"SJIS")).getBytes("EUC-JP");
    }
    
    //Shift_JISで書かれたファイルをEUC-JPに変換してファイル出力
    static public void file(File sjis,File eucjp) throws Exception{
        //効率を上げたい場合はBufferedReader等でラップする
        Reader in=new InputStreamReader(new FileInputStream(sjis),"SJIS");
        Writer out=new OutputStreamWriter(new FileOutputStream(eucjp),"EUC-JP");
        
        int c;
        while((c=in.read())!=-1) out.write(c);
        
        out.close();
        in.close();
    }
}

Haskellの文字や文字列にはエンコーディングの概念はありませんので,解釈するとすれば,SHIFT-JISバイト列からEUC-JPバイト列への変換ということになるかなぁ.

バイト列を扱うには,Data.ByteString.Lazy モジュールを使います.またバイト列でのエンコーディング変換には,iconv というパッケージにある.Codec.Text.Iconv モジュールを使います.

1
2
3
4
5
6
7
8
9
import Data.ByteString.Lazy as B
import Codec.Text.IConv

sjis2eucjp :: ByteString -> ByteString
sjis2eucjp = convert "SHIFT-JIS" "EUC-JP"  -- エンコード変換関数

main :: IO ()
main = B.interact sjis2eucjp -- 標準入力からのバイト列をエンコード変換して
                             -- 標準出力へ書き出す

すいません。 匿名で書いたのですが、 変更したかったので、 ついでにIDを作成してみました。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
//Model
function encSjistoEuc($str)
{
    if ($str != "") {
        $str = mb_convert_encoding($str, "EUC-JP", "SJIS");
    }
    return $str;
}

//この方がすっきりですね。
シンプルに。
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
#1. convert encoding of string from sjis to eucjp
sjis2euc.character <- function(str){
    iconv(str, "sjis", "eucjp")
}

#2. convert encoding of file from sjis to eucjp
sjis2euc.file <- function(infile="sample.sjis", outfile="sample.eucjp"){
    fr <- file(infile, "r", encoding="sjis")
    fw <- file(outfile, "w", encoding="eucjp")
    cat(readLines(fr), sep="\n", file=fw)
    close(fr)
    close(fw)
}
1はNKFが標準でついているのでそれで。
2は日本語っぽく。
1
2
3
4
5
●SJIS2EUC(Sを)
    Sを"--ic=Shift_JIS --oc=EUC-JP"でNKF変換
    //SをEUC変換
#-----------------------------------------------
"sjis.txt"を開いてEUC変換して"euc.txt"に保存

1:Pythonの場合、バイト列を「かってに変換せずに生のまま」扱うこともでき、それを「ユニコード文字列」に変換することもできます。

2:codecs.openでファイルを開けば指定したコーデックで読み書きをします。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
>>> "こんにちは"
'\x82\xb1\x82\xf1\x82\xc9\x82\xbf\x82\xcd'
>>> _.decode("sjis")
u'\u3053\u3093\u306b\u3061\u306f'
>>> _.encode("euc-jp")
'\xa4\xb3\xa4\xf3\xa4\xcb\xa4\xc1\xa4\xcf'

>>> import codecs
>>> codecs.open(r"c:\tmp.txt", encoding="sjis").read()
u'\u3053\u3093\u306b\u3061\u306f'
>>> codecs.open(r"c:\tmp_euc.txt", "w", encoding="euc-jp").write(_)

Index

Feed

Other

Link

Pathtraq

loading...