どーも!

たかぽんです!

先日ファイルのパスから拡張子を除いたファイル名をどうにかして取得したい!

ってなったので、調べてみました!

やりたいこと

まず、やりたいことはファイルのパス(dir1/dir2/hoge.jpgみたいなやつ)から、"dir1/dir2/"と".jpg"を除いた"hoge"を取得したかったんです。

いろんな方法で取得できそうだったので、色々試してみました!

早速ですが、以下にコードをはっつけておきます!

(function(){

  // 入力されるオリジナルのパス
  const path = 'hoge/huga/hugo/filename.jpg';
  console.log('original = ' + path);          // hoge/huga/hugo/filename.jpg
  
  // さくっとファイルのパス、拡張子、ファイル名を取得してみる
  match = path.match(/[^/]+$/);
  filename = path.match(/([^/]*)\./)[1];
  extend = path.match(/[^.]+$/);
  console.log('fileinfo = ' + match);         // hoge/huga/hugo
  console.log('filename = ' + filename);      // filename
  console.log('extend   = ' + extend);        // jpg

  // メソッドを活用して取得したい場合
  const resultPath = path.split("/").reverse().slice(1).reverse().join("/");
  var filename = path.split("/").reverse()[0].split('.')[0];
  var extend = path.split("/").reverse()[0].split('.')[1];
  console.log('result1  = ' + resultPath);    // hoge/huga/hugo
  console.log('filename = ' + filename);      // filename
  console.log('extend   = ' + extend);        // jpg
  
  // メソッドの手法の過程確認
  const split = path.split('/');
  console.log('split    = ' + split);         // hoge,huga,hugo,filename.jpg

  reverse = split.reverse();
  console.log('reverse  = ' + reverse);       // filename.jpg,hugo,huga,hoge

  rss = reverse.slice(1);
  console.log('sliced   = ' + rss);           // hugo,huga,hoge

  // メソッドの手法にて、ファイルネームのみor拡張子のみが欲しい場合
  const fileinfo = reverse[0].split('.');
  console.log('dot      = ' + fileinfo);      // filename,jpg
  console.log('filename = ' + fileinfo[0]);   // filename
  console.log('extend   = ' + fileinfo[1]);   // jpg

  // 正規表現を使った別の手法
  filename = path.match(".+/(.+?)([\?#;].*)?$")[1];
  extend = path.match(".+/(.+?)\.[a-z]+([\?#;].*)?$")[1];
  console.log('filename = ' + filename);      // filename
  console.log('extend   = ' + extend);        // jpg

  // 拡張子の別の取得方法
  var inputFilenName = "hoge.mp4";
  console.log(inputFilenName.lastIndexOf('.') + 1) // 5
  console.log(inputFilenName.substring(inputFilenName.lastIndexOf('.') + 1)); // mp4
}()); 

さて、長くなってしまいましたが、上記のような形です。

手遊び感覚で色々やっていたの汚いですが...w

色々と調べていると正規表現を使う手法とメソッドを使う手法になんとな〜く分かれていた印象です。

以下から試すことが可能ですので、よければ触ってみてください。

正規表現を使う方法

まずは正規表現を使う方法について簡単に説明しておきます。

ただし・・・!

筆者いまだに正規表現いまいち理解できていないので...orz

参考程度にお考えください...

match = path.match(/[^/]+$/);
filename = path.match(/([^/]+)\./)[1];
extend = path.match(/[^.]+$/);

上記三行が簡易版の正規表現のものです。

最初のものは"/"以外の文字が繰り返されていて、尚且つ終端に接している文字列を示しています。

つまり、"hoge/huga/hugo/filename.jpg"の終端から"/"を含まない文字をみていけば..."filename.jpg"が当てはまりますね。

filenameの取得は、末尾がドットで、その手前にある"/"を含まない文字の塊ですね。

ただし...それだと"filename."になるのでは?と...

その通りです。

そこで、キャプチャ"()"を使用しています。

エスケープした"\."はドット文字を表しており、その手前にあった"/"を含まない文字列をキャプチャ(記憶)しておきます。

また、キャプチャした値は出力として配列に返され、path.match(/([^/]+)./)[0]には"filename.", path.match(/([^/]+)./)[1]には"filename"が格納されます。

それによって、ファイルネームだけ取り出している形です。

最後は"/[^.]+$/"で、.を除く終端に繋がる文字列を探しています。

ただ、上記に関しては筆者がごちゃごちゃいじくりまわして、うまくいくじゃ〜ん!

ってなっただけなので、一般的な例外等(変なファイル名やディレクトリ名等々)に対応できていない可能性が高めです...w

一応"."が拡張子以外で入るファイル名("hoge/huga/file.name.jpg")は試してみて大丈夫そうでしたが、ディレクトリに"."が入る(ho.ge/huga/filename.jpg)とおかしくなりました...orz等。

(そもそもそんなディレクトリ構成にできるのかとか調べてませんが...w)

また、色々調べていると以下の方法でもできるそうです。

  filename = path.match(".+/(.+?)([\?#;].*)?$")[1];
	extend = path.match(".+/(.+?)\.[a-z]+([\?#;].*)?$")[1];
  console.log('filename = ' + filename);      // filename
	console.log('extend   = ' + extend);        // jpg

探せばもっとたくさんありそうですね...!

ただ、個人的に正規表現は読みづらい...ので、次に解説するメソッド主体の方が好みです...w

メソッドを活用してファイル名を取得する

メソッドを使うパターンですね。

	const resultPath = path.split("/").reverse().slice(1).reverse().join("/");
	var filename = path.split("/").reverse()[0].split('.')[0];
	var extend = path.split("/").reverse()[0].split('.')[1];

詳細はコードに途中過程も合わせて記載していますが、簡単に解説しておきます。

まず、元のpathを"/"で区切り、配列化します。

"hoge/huga/hugo/filename.jpg" => [hoge, huga, hugo, filename.jpg]

その後、配列の中身を反転させます。

[hoge, huga, hugo, filename.jpg] => [filename.jpg, hugo, huga, hoge]

次に、sliceで二番目の要素以降のもののみ取り出します。

[filename.jpg, hugo, huga, hoge] => [hugo, huga, hoge]

再度反転させて、"/"で結合します。

[hugo, huga, hoge] => [hoge/huga/hugo]

これでファイルのある親ディレクトリのパスが取得できます。

次はfilenameとextendです。

これらは、先ほどの途中から派生する形です。

反転をすると、一番目の要素にファイル名(拡張子含む)が入ります。

なので、"reverse()[0]"でそれを取得します。

[filename.jpg, hugo, huga, hoge] => [filename.jpg]

拡張子を含むファイル名が欲しい方はこの時点で終了でOKです。

さらに分解したい人は引き続き、今度は”."で区切ります。

[filename.jpg] => [filename, jpg]

あとは一番目の要素がファイル名、二番目の要素が拡張子となっています。

注意すべきことは、先ほど正規表現の方でも少し触れましたが、ファイル名にドットが入っている場合、上記ではうまくいきません。

それにも対応したい場合...

例えば"file.name.jpg"というファイルだったとしたら、ドットで区切るまではOKです。

[file.name.jpg] => [file, name, jpg]

そしてさらに反転をすることで、拡張子が一番目の要素になります。

[jpg, name, file]

さらに、slice(1)で二番目の要素以降を取り出し、再度反転させて"."で結合すれば拡張子を除いたファイル名になるはずです。

[name, file] => [file, name] => [file.name]

できあがり!

番外編

ちなみに、上記で色々と調べはしたんですが...w

僕の場合、node.jsを使用していて、とても便利なメソッドがあったので、結局そのメソッドを使用しました...w

以下、公式のリファレンスを貼っておきますね!

使い方は難しくないので、英語ができずとも、なんとなくで読んでいただければわかるかと!

少し下にいけば拡張子だけ、ファイル名だけ等もありますのでもしもnode.jsが入っている人は検討の価値有りかと!

追記

先日拡張子の取得をしているコードで、あ、これ楽でわかりやすい!

というものがあったので、それを追記しておきます。

var inputFilenName = "hoge.mp4";
console.log(inputFilenName.lastIndexOf('.') + 1) // 5
console.log(inputFilenName.substring(inputFilenName.lastIndexOf('.') + 1)); // mp4

方法としては文末から最初に見つかった"."のインデックスを記録し、それに1を足したもの('.'の右隣にある文字)を取得します。

これが拡張子の一番最初のインデックスになるわけですね(今回はmのインデックス)。

そして、元の文字列からそのインデックスまでの文字列を引き算してあげると...

つまり、"hoge.mp4" - "hoge." で、結局 "mp4"が残るわけです!

文末に一番近いドットを探すので、ファイルパスを含むものでも、ファイル名だけでも拡張子だけ取得することができます!

まとめ

ファイルパス取得に関して簡単に調べてみました!

色々と方法があり、おそらく正規表現での調べ方に関しては何通りも作れる気がします...

ただ、僕自身まだ全然正規表現理解できてないなぁと...

あんまり好きでは無いですが、読めるようにならねば...

おすすめの記事