PERLのchop;とs/\n//;とtr/\n//;の違いは?

[上に] [前に] [次に]
ぱーるぱる 2000/06/01(木) 11:11:50
みなさん、こんにちわ。
いつもCGIを組むときに勉強させて頂いてます。
私もちょっと自分では解決できない問題があったので質問させてください。

『問題のソース』
open(ID,"<$id_file");
@indata = <ID>;
close(ID);

# 取り込んだデータの一行目を獲得する。
local($mae,$usiro) = split(/,/,$indata[0]);

# $usiro = ~s/\n//;
# $usiro = ~tr/\n//;
chop($usiro);

ある
0001,00000001
0002,00000002
という感じでコンマ区切りでデータが入っているファイルがあって、
それを最初のところで読みこみました。

次にコンマでスプリットしました。
それで改行を取りたかったので、最初に
s/\n//;でやってみたところ、$usiroには4294967295とかいう意味不明な数字が入ってしまったので

次に
tr/\n//;でやってみたところ上記の4で始まる怪しい数字がまたも$usiroに入ってきました。

そこで
chop($usiro);で試した所、やっとこさ$usiroに00000001が入ってくれました。

この3つの違いはなんなのかどなたかご教授頂けないでしょうか?
あの怪しい数字もどこからでてきたのかさっぱりわかりません。
どうかよろしくお願い致します。

バギンズ 2000/06/01(木) 11:34:29
>$usiro = ~s/\n//;
$usiro =~ s/\n//;
なのでは?

~ って何だったけ...あービット反転か。
$_の内容(不明)から\nを削除した結果のビット反転が4294967295
って事ですかね。

悪魔のZ 2000/06/01(木) 11:51:39
>4294967295
unsigned long(符号なし32Bit)の11111111111111111111111111111111ですよね?

ぱーるぱる 2000/06/01(木) 11:59:38
すばやいレスポンスありがとうございます。
単純に文法のミスだったのですね・・・(反省)。
ビット反転という言葉は始めて聞いたので、これから
GOOで検索してみます。

バギンズさん、悪魔のZさん、本当にどうもありがとうございました。

ぱーるぱる 2000/06/01(木) 12:00:30
[[解決]]
すみません。
チェックするの忘れてしまいました。
本当にありがとうございました。

バギンズ 2000/06/01(木) 12:04:42
>悪魔のZさん
$_は0でしたか(^_^;)

>ぱーるぱるさん
ビット反転は2進数にした場合の0と1を反転させるって事です。

0011 の全ビットを反転すると
1100 になります。

1010 の全ビットを反転すると
0101 になります。

わかりますか?

S-pore [HomePage] 2000/06/01(木) 12:09:25
> $_の内容(不明)から\nを削除した結果のビット反転が4294967295

厳密に言うと,
・$_ に \n が含まれていれば 1 のビット反転
・$_ に \n が含まれていなければ 0 のビット反転
かな。

chop() のことで前々から気になってたことを便乗質問。(^^;

$nazo = "謎ですね♪\n";
print length($nazo) . "\n";
chop($nazo);
print length($nazo) . "\n";

これを実行すると,
11
9
って具合で,chop() で2バイト削れてしまいます。
(ソースの漢字コードはShiftJIS)

$nazo = "aabbccddee\n";
とかにすると,ちゃんと
11
10
になります。これって何故なんでしょう?(^^;

ぱーるぱる 2000/06/01(木) 12:17:37
バギンズさんへ
フォローまでして頂いて本当に感謝です。
今もGOOで検索してみたのですが、2進数を反転したのに1を足すと補数になるとかそういった事がかいてありました。
ビット反転は補数を求めるときに使うんですね。
やっと理解できました。
本当にありがとうございました。

バギンズ 2000/06/01(木) 12:38:07
>2進数を反転したのに1を足すと補数になる
2の補数ですね。
数値のプラスとマイナスを反転するときの方法です。
でも普通は−1をかければいいだけ(笑)

では、ビット反転(not)を何に使うかと聞かれても悩みますが、
ビット操作で必要になった時に使えばいいかなみたいな...(^_^;)

もた 2000/06/01(木) 13:50:52
(別スレッドにした方がいいでしょうか?)
>$nazo = "謎ですね♪\n";
>print length($nazo) . "\n";
>chop($nazo);
>print length($nazo) . "\n";
>これを実行すると,
>11
>9
>って具合で,chop() で2バイト削れてしまいます。
>(ソースの漢字コードはShiftJIS)

試してみましたが1バイトしか削れませんでした(11→10)
jperl 5.005_03 EUC version (on Linux)
ActivePerl 522 (on Windows)
です。

悪魔のZ 2000/06/01(木) 14:14:03
> これを実行すると,
> 11
> 9
> って具合で,chop() で2バイト削れてしまいます。
> (ソースの漢字コードはShiftJIS)

テストして検証したわけではないですけど
SJISの改行は CR LF なので
そのせいのような気がします
http://www.tohoho-web.com/wwwxx011.htm

Syn [E-Mail] 2000/06/01(木) 14:45:01
> SJISの改行は CR LF なので

んなことはありません。
改行コード (\n) はプラットフォーム依存ですが、文字セットには
依存しません。
それに、それだと "aabbccddee\n" のときの動作を説明できません。

ところで、 chop って末尾の \n のみを取り去るんでしたっけ?
単に末尾の 1 バイトを削除するだけだと思ってました。

Fuji.♪ [E-Mail] [HomePage] 2000/06/01(木) 14:50:57
chomp()は末尾\nを、chop()は無条件に末尾1バイトだと思いましたが・・・。 (^^;

バギンズ 2000/06/01(木) 14:51:38
>S-poreさん
私はローカル(DOS窓)で試しましたが、1バイトしか削れませんでした。

>悪魔のZさん
>SJISの改行は CR LF なので
勘違いしているようですが、漢字コードと改行コードは関係ありません。
\n は LF だけです。

11→9って事は、「♪」の2バイト目まで削れてしまったって事でしょうか?
Perlのバージョン(バグ)の問題?

悪魔のZ 2000/06/01(木) 15:01:48
うぅぅ失礼しました…

ところで、
lengthじゃなくて文字列自身をPRINTすると何て出るんですか? > S-poreさん

S-pore [HomePage] 2000/06/01(木) 15:02:29
すみません,なんか Perl のバグみたいです。(^^;
別のサーバーで試してみたら,(Perl4 でも Perl5 でも)
11
10
になってくれました。

ちなみに,このバグ(?)が発生したのは,

This is perl, version 5.004_04 built for i386-linux
Japanization patch 4 by Yasushi Saito, 1996
Modified by Hirofumi Watanabe, 1996, 1997
jperl5.004_04-971016
EUC version

ってやつです。(某 PR*X さんのサーバーに入っていたもの)

> SJISの改行は CR LF なので

これって,「M$(謎)環境では」じゃないですか?
私の場合は,バグが発生したサーバーもうまく動いたサーバーも
両方 UNIX 環境なので・・・。

> ところで、 chop って末尾の \n のみを取り去るんでしたっけ?
> 単に末尾の 1 バイトを削除するだけだと思ってました。

chop() はそうですね。
chomp() なら改行コード(どの改行コードを使うかを定義可能)しか削りません。

S-pore [HomePage] 2000/06/01(木) 15:15:06
[[解決]]
おっと,ここを開きながら検証やらなんやらをしていたら
たくさんのお返事が・・・。(^^;

> lengthじゃなくて文字列自身をPRINTすると何て出るんですか? > S-poreさん

「謎ですね・」みたいな感じです。
最後の点は,「♪」の1バイト目でしょう。(ネスケで見るとそうなりました。)

こういうことがあってから,私は chop() を使わなくなり,
かといって chomp() は Perl5 でしか使えないからこれもパスで,
s/\n$//; は,正規表現は重いという偏見(?)でやはり遠慮。
結局 $nazo = substr($nazo, 0, length($nazo) - 1); でやるようにしてます。(^^;
(実は $nazo = substr($nazo, 0, -1); でもいいのですが)

というわけで,どうでもいいことを質問してしまってすみませんでした。
たくさんのお返事をいただいたのに結局「バグ」っぽくて申し訳ないです。(^^;

ぱーるぱるさんの疑問も私の疑問も解決したところで,
改めて[[解決]]!

Syn [E-Mail] 2000/06/01(木) 15:16:01
けっきょく jperl のバグってことですかねぇ。 使ったことがない
のでよくわかりませんが。

> \n は LF だけです。

んー、これも違うような。
\n はプラットフォームによって定義が違います。
UNIX なら \n = \x0A
Windows なら \n = \x0D\x0A
Mac なら \n = \x0D
です。

> chomp()は末尾\nを、chop()は無条件に末尾1バイトだと思いましたが・・・。 (^^;

なるほど、そんな違いがあったのですか。
わたしは chomp は chop のエイリアスだと思ってました(^_^;
Fuji.♪ さん、 S-pore さん、ありがとうございました。

S-pore [HomePage] 2000/06/01(木) 15:23:34
> \n はプラットフォームによって定義が違います。
> UNIX なら \n = \x0A
> Windows なら \n = \x0D\x0A
> Mac なら \n = \x0D

むぅぅ,個人的にはコレもちょっと・・・。(^^;
たしかに(テキストモードなら)自動変換のおかげで
「\n = \x0D\x0A」のように見えますが,
あくまで \n は \x0A だと思ってます。(そして, \r は \x0D です。)

2000/06/01(木) 15:24:06
> んー、これも違うような。
> \n はプラットフォームによって定義が違います。
いや、\nはあくまでLF(0x0a)です。CR(0x0d)は\rです。
Cのライブラリ(PerlもCで書かれているはず)プラットフォームに
あわせて\nを置換してくれているだけです。

バギンズ 2000/06/01(木) 15:24:08
Perl上では

\n は LF
\r は CR

だと思いますが...

たしかJavaScriptも同じだったな。

フォームのTEXTAREAから送られてきた改行コードは、ブラウザ側の
プラットフォームに依存すると思いますが...

バギンズ 2000/06/01(木) 15:30:26
いかん、ちんたら書いてると、すぐに先を越されてしまう。(^_^;)

あっそうそう、
>$usiro = ~s/\n//;
で、$_に文字列を入れて検証してみたのですが、

$usiro = s/\n//;

だと、
$_ に \n が含まれている場合は 1
含まれていない場合は、ヌルストリングでした。

で、
$usiro = ~s/\n//;

にすると、S-poreさんが書いた
>・$_ に \n が含まれていれば 1 のビット反転
>・$_ に \n が含まれていなければ 0 のビット反転
の結果となりました。

Perlってヌルストリングを数値として判断する場合は完全に 0 って判断しているんですね。

Syn [E-Mail] 2000/06/01(木) 15:54:40
あれれ、改行コードの解釈をわたしが間違えていたのでしょうか。
しかし、

> いや、\nはあくまでLF(0x0a)です。CR(0x0d)は\rです。
> Cのライブラリ(PerlもCで書かれているはず)プラットフォームに
> あわせて\nを置換してくれているだけです。

これだと結局 Perl 上での \n はプラットフォームによって可変で
あるとゆー結論になるような気がしているのですが。
CGI ML での大崎さんの発言を引用しますと、

> From:Hiroki Ohzaki <ohzaki@iod.ricoh.co.jp>
> Subject:[cgi:21913] Re: 正規表現で改行を削除できない
> Date:Thu, 11 May 2000 11:14:53 +0900
> \r や \n というのは Perl という言語の中で復帰文字や改行文
> 字の概念を表わしたものであって,\r が \x0D,\n が \x0A に
> なるというのはあくまでもプラットフォーム依存です.ですから,
> 「Mac での改行コードは,Win や UNIX の perl での \r に相当
> します」という表現を私は使っています.
:
> 概念の話をするならば,改行コード(改行文字)は Mac => \n,
> Win => \n, UNIX => \n となり,実際のコード値の話をするなら
> ば Mac => \x0D, Win => \x0D\x0A, UNIX => \x0A となります.

とゆーことなのですが…

S-pore [HomePage] 2000/06/01(木) 16:14:26
> これだと結局 Perl 上での \n はプラットフォームによって可変で
> あるとゆー結論になるような気がしているのですが。

なんか,正しいような正しくないような・・・言葉のあやってやつでしょうか。(^^;

$nazo = "nazo\n";
$nazo =~ s/./sprintf("%02X ", ord($&))/egs;
print $nazo;

このプログラムを実行すると,たぶんプラットフォームにかかわらず
6E 61 7A 6F 0A
がでてくると思います。
つまり,Perl(C言語でもそうですが)上では
プラットフォームにかかわらず改行コードを \n (\x0A) で扱えるようになっていて,
この \n を標準出力やテキストモードでオープンされたファイルに書き出すときに,
たとえばMSプラットフォームなら \n → \r\n(\x0D\x0A) という変換を
自動的にしてくれるわけです。
仮にこの変換をしてくれないと,
UNIX環境では
print "Hello!\n";
MS環境では
print "Hello!\r\n";
というふうに,プラットフォームに応じてプログラムを書き換える必要がでてきます。
その手間を省くために,Perl や C言語のプログラム上では,
改行コードを \n で一様に扱えるようになっているわけです。

バギンズ 2000/06/01(木) 16:21:20
> Cのライブラリ(PerlもCで書かれているはず)プラットフォームに
> あわせて\nを置換してくれているだけです。

「Cのライブラリ」という部分がみそです。
出力時に変換が行なわれるのです。
コード上では \n = LF(0x0a)のままのはずです。

Syn [E-Mail] 2000/06/01(木) 16:30:46
なんだか \n の話をえんえんとしているのももうしわけない気持ち
になってきましたが(^_^;

> $nazo = "nazo\n";
> $nazo =~ s/./sprintf("%02X ", ord($&))/egs;
> print $nazo;
> このプログラムを実行すると,たぶんプラットフォームにかかわらず
> 6E 61 7A 6F 0A
> がでてくると思います。

Windows 2000 + ActivePerl 613 でそうなることを確認しました。
つまり、リテラルとしての \n は \x0A 固定で、 I/O 部分で変換
がかかってるってことですね。
どうもありがとうございます m(_ _)m

S-pore [HomePage] 2000/06/01(木) 16:50:51
> \r や \n というのは Perl という言語の中で復帰文字や改行文
> 字の概念を表わしたものであって,\r が \x0D,\n が \x0A に
> なるというのはあくまでもプラットフォーム依存です.

まあ確かにそうですねぇ・・・。
\n = \x0A である必然性はないかも。
仮に \x0B を出力すると改行するプラットフォームができたとして,
その上で走る Perl を設計するとした場合,
私なら,Perl 上では \n (= \x0A) で扱えるようにして,
外部との入出力時に \x0A ←→ \x0B のフィルタリングをするようにするでしょうけど,
人によっては \n を即 \x0B に置き換えてしまうような Perl
(つまり ord("\n") == 0x0B)を設計するかもしれないし,
そうされても何も文句はいえないでしょうね。

ただ,少なくともMS環境でもUNIX環境でもMAC環境でも
\r = \x0D, \n = \x0A となっているので,
ここまで話を厳密にする必要はないような・・・。(^^;

> 「Mac での改行コードは,Win や UNIX の perl での \r に相当
> します」という表現を私は使っています.

なんかここだけえらく控えめな言い方になってますが,
これは100%正しいでしょう。(たぶん)

> 概念の話をするならば,改行コード(改行文字)は Mac => \n,
> Win => \n, UNIX => \n となり,実際のコード値の話をするなら
> ば Mac => \x0D, Win => \x0D\x0A, UNIX => \x0A となります.

なんか,どこをつっこんでいいのやら分かりませんが,
プログラム上ではどの環境でも「改行文字 = \n」で扱えるけれども,
外部(ファイルとか標準入出力とか)では改行コードはプラットフォームに依存する。
厳密に議論するならたぶんこんな感じでしょうか。

\n = \x0A と決めつけるのはよくない,ということはわかりますが,
かといって「Win上では \n = \x0D\x0A である」というのには賛成できません。

S-pore [HomePage] 2000/06/01(木) 16:55:20
> プログラム上ではどの環境でも「改行文字 = \n」で扱えるけれども,

この \n はあくまでも \x0A ね。(UNIX / Win / MAC に関する限り)

2000/06/01(木) 17:00:38
> ただ,少なくともMS環境でもUNIX環境でもMAC環境でも
むしろ、ASCII環境という感じがしますが。

S-pore [HomePage] 2000/06/01(木) 17:18:32
> むしろ、ASCII環境という感じがしますが。

そうですね〜。

[上に] [前に] [次に]