助け合いフォーラム

LPIC

LPIC Lv1-101(Ver5.0)
問題ID : 3688
問題を開く
Windowsのメモ帳で作成したテキストファイル「file1.txt」をLinuxで正しく扱うために、改行コードを変換して「file2.txt」として保存したい。適切なコマンドはどれか。(2つ選択)

正解

tr -d '^M' < file1.txt > file2.txt

sed s/^M//g file1.txt > file2.txt

解説

改行コードと代表的なOSの組み合わせは以下のとおりです。
・CRLF(\r\n):Windows
・LF(\n):Unix OS(Linux, Mac OS Xなど)
・CR(\r):古いMacOS(バージョン9まで)

上記の通りWindowsとLinuxでは改行コードが異なるため、テキストファイルの扱いが問題になる場合があります。CRLF(\r\n)からCR(\r)を取り除くことでLinuxの認識できる改行コード:LF(\n)のみにでき、正しく扱うことができるようになります。(類似問題ID:34286をご参照ください。)
また、CR(キャリッジリターン)は「^M」という制御コードでも表されるため、Windowsで作成したファイルをLinuxのエディタで開くと末尾に「^M」が表示されることがあります。よって、制御コード「^M」を取り除くことでもLinuxの改行コードLF(\n)に変換できます。
なお、Linux上で制御コード「^M」を入力するには、Ctrl+V、Ctrl+Mを続けて押下します。

選択肢を1つずつ確認してみます。

・tr -d '^M' < file1.txt > file2.txt
trコマンドの-dオプションで制御コード「^M」を削除しています。リダイレクト(<)によって「file1.txt」を入力とし、削除した結果を「file2.txt」に出力(>)しています。正しいコマンドです。

・tr -d 'CR' < file1.txt > file2.txt
「CR」という文字列を削除しているだけなので改行コードは変わりません。よって誤りです。

・sed s/^M//g file1.txt > file2.txt
sedコマンドで「file1.txt」中の全ての制御コード「^M」を空白文字に置換(つまり削除)して「file2.txt」に出力しています。正しいコマンドです。

・sed s//^M/g file1.txt > file2.txt
変換前の文字列が空白なのでエラーになります。よって誤りです。

・sed s/CRLF/LF/g file1.txt > file2.txt
全ての「CRLF」という文字列を「LF」という文字列に変換しているだけなので改行コードは変わりません。よって誤りです。

したがって正解は
・sed s/^M//g file1.txt > file2.txt
・tr -d '^M' < file1.txt > file2.txt
です。

以下は実行例です。

参考

【正規表現】
正規表現とは、文字列の特定のパターンを認識する為に使用する表現方法です。文字列の検索や置換などを行う際に利用します。正規表現には基本正規表現(BRE: Basic Regular Expression)と拡張正規表現(ERE: Extended Regular Expression)があります。

以下は主な正規表現とその使用例をまとめたものです。


正規表現には次のような基本の概念があります。
・特殊文字
「|」や「\(エスケープ文字)」などのように特殊な意味を持つ文字のことです。

・文字クラス
「[ ]」内の文字集合のことです。

・数量詞
「*」や「+」などのように直前の文字の繰り返し回数を示す文字のことです。

・アンカー
「^」や「$」などのように文字列内での位置を示す文字のことです。

なお、正規表現の「*」と、シェルによって解釈されるメタキャラクタの「*」では意味が異なるので注意してください。シェルは「*」を0文字以上の文字列と解釈します。
正規表現は明示的に「'」(シングルクォーテーション)や「"」(ダブルクォーテーション)の引用符で囲う事ができます。これらの引用符で囲まれた正規表現の記号は、シェルにメタキャラクタとして扱われなくなります。

正規表現を利用する主なコマンドには「sed」「grep」があります。

■sed
ファイルや標準入力の内容を編集して表示するコマンド

例)「test.txt」ファイルの「#」から始まる行を削除して出力する場合
 $ sed '/^#/d' test.txt

■grep
ファイルや標準入力から、検索パターンにマッチする文字列を含む行を抽出するコマンド

例)1から5までのいずれかの文字がある行を「test.txt」ファイルから抽出する場合
 $ grep '[1-5]' test.txt

[基本正規表現と拡張正規表現]
基本正規表現と拡張正規表現の違いに注意してください。

grepコマンドは、-Eオプションを付けない場合は検索パターンを基本正規表現と判断します。
grepコマンドの検索パターンで拡張正規表現を使うには、-Eオプションを指定します(egrepコマンドと同様)。
例)拡張正規表現「+」を使った抽出


なお、拡張正規表現の「?」「+」は、基本正規表現では「\?」「\+」で同様の指定ができます。
例)基本正規表現で「+」を扱う場合
上に戻る

「改行コード」と改行をしないファイルの扱いについて

投稿日 2025/04/17

上記の通りWindowsとLinuxでは改行コードが異なるため、テキストファイルの扱いが問題になる場合があります。CRLF(\r\n)からCR(\r)を取り除くことでLinuxの認識できる改行コード:LF(\n)のみにでき、正しく扱うことができるようになります。(類似問題ID:34286をご参照ください。)
また、CR(キャリッジリターン)は「^M」という制御コードでも表されるため、Windowsで作成したファイルをLinuxのエディタで開くと末尾に「^M」が表示されることがあります。よって、制御コード「^M」を取り除くことでもLinuxの改行コードLF(\n)に変換できます

浅学で恐縮ですが、本問題の解説を見て2点ご教示いただきたいです。
解説文に上記のような記載があります。

①改行コードやCRLF、LFという単語の指す範囲についてもわからなくなりました。
「改行コード」というのは「CRLF」や「LF」といったものを指し、「\r\n」や「\n」などが実際の制御コードだという認識でおりました。
しかし、今回このような表現になっているということは、
「改行コード」という言葉は、改行方式の仕様と制御コードどちらのことも指せる表現なのでしょうか?

②改行していないのにLFに変換されるということ。
trやsedを使い「^M」を取り除くことで確かにLinux上で正確に認識ができるようになるとは思うのですが、
本問題の場合は改行をそもそもしていないテキストの文章に変換されるという解釈をしています。(\r\nから\rを取り除くとかならCRLF→LFに変換されたという意味は分かります。)
私としては、「OSによって改行コードが異なり、その改行コードごとに異なる制御コードを使うため、その制御コードが埋め込まれているファイルを別のOSでは認識できない」くらいの認識だったのですが、改行するための制御コードを含まないファイルにも改行コードの概念が存在しているということでしょうか?

2025/04/17 20:28

厳密にいうかどうかってところではありますね。

まず「改行コード」自体は厳密には「改行を意味する制御文字」という意味で間違いありません。Wikipedia に掲載されている表にある16進数の「0D(CR)」「0A(LF)」が該当します。
https://ja.wikipedia.org/wiki/ASCII

そして別の意味としては「システムのデフォルト」で使われる改行を意図する制御文字、があります。これが解説の

Linuxの改行コードLF

の意味するところかと。基本的には「システムのデフォルト」と「文字列中に含まれる制御文字」の2つのどちらも「改行コード」と表現することが多いです。文脈でどちらの意味かを判断する感じですかね。

で、2つ目なのですが、こちらはちょっと質問の意図がわからないです。

本問題の場合は改行をそもそもしていないテキストの文章に変換されるという解釈をしています。

file1.txt や file2.txt が「改行をそもそもしていないテキスト」というのは問題からは読み取れないため、どういうイメージを持たれたのかが分かりませんでした。

例えばですが、3行の CRLF で改行してある crlf.txt ファイルを作ってみたらこんな感じです。 od -t x1 は1バイトずつ16進数で表示してくれるコマンドです。

$ cat crlf.txt
test
line
ping-t
$ od -t x1 crlf.txt
0000000 74 65 73 74 0d 0a 6c 69 6e 65 0d 0a 70 69 6e 67
0000020 2d 74 0d 0a
0000024

この中の 0d 0a が CRLF 改行です。これを正解のコマンドで lf.txt に変換してみます。(環境的な制約で ^M がうまく入力できなかったので \r を使ってます)

$ tr -d "\r" < crlf.txt > lf.txt
$ ls -l
total 8
-rw-rw-r-- 1 user user 20 Apr 17 20:16 crlf.txt
-rw-rw-r-- 1 user user 17 Apr 17 20:19 lf.txt
$ od -t x1 lf.txt
0000000 74 65 73 74 0a 6c 69 6e 65 0a 70 69 6e 67 2d 74
0000020 0a
0000021

3行からそれぞれ 0d を削除したので3バイト減っていますね。そして 0d 0a0a になっています。この問題でやっている操作は単純に「改行コードがCRLFのシステムで作成されたテキストファイルからCRを削除する」ということでしかないので、そんなに難しく考えなくても良いかなーと思います。


コメント

t t-funaki

2025/04/18 09:42

ご返信ありがとうございます。 1つ目について、「システムのデフォルト」と「文字列中に含まれる制御文字」両方表せること理解いたしました。 2つ目については、すみません。私が誤認をしていたようです。 \r\n = ^Mだと読み間違えていました。 そのため意味がよく分からなくなっていたのですが、 \r = ^Mで、元のファイルは^M\n(表現として正しいかわかりませんが)の状態になっていたのを\nにした。\nが残っているため、改行コードLFに変更したという表現になっているということですね。 お騒がせして申し訳ありませんでした。

a arashi1977

2025/04/19 17:16

そのご認識であってます! CRLFとは \r\n のことであり、別の表現では ^M^J です。そこから CR(\r, ^M) を除去して LF(\n, ^J) のみにするということです。

この返信に対して
コメントを記入できます

この投稿に対して返信しませんか?