複雑な条件のファイル検索・置換を楽にするライブラリ「go-rewrite」を作った
tl;dr
- 複雑なファイル検索・置換は grep、sed だけでは難しい場合がある
- そういう場合は コマンドラインツールなどで自作するしかない
- 検索・置換ルールだけに集中したコーディングをするためのライブラリとして「go-rewrite」 を作った
- 「go-rewrite」 はディレクトリの再帰的な走査と検索・置換ルールの適用を並列に行う
インストール
go get github.com/yoheimuta/go-rewrite
検索・置換ルール
go get github.com/yoheimuta/go-rewrite
検索・置換ルール
「go-rewrite」 は Rule インターフェースを定義している。
ライブラリを利用するコマンドラインツールはこのインターフェースを実装するだけでいい。
- Filter はファイル名を使って対象のファイルを絞り込むメソッドを表す。
- Mapping はファイルのコンテンツを使って新しいコンテンツを生成するメソッドを表す。
- Output は新しいコンテンツを利用するメソッドを表す
Filter が検索処理、Mapping と Output が置換処理にそれぞれ対応する。
// Rule is the rule for rewrite.
type Rule interface {
// Filter filters the file using the filepath.
Filter(filepath string) (isFilter bool, err error)
// Mapping maps the file content with new one.
Mapping(content []byte) (newContent []byte, isChanged bool, err error)
// Output writes the content to the file.
Output(filepath string, content []byte) error
}
例
次のような .swift ファイルを検索して、//
を ///
に置換したい。ただし、func
class
var
の //
だけを対象にしたい。コメントは複数行ある場合があるのでそれも考慮しないといけない。
//
// Int64+JPY.swift
// Hoge
//
// Created by FugaFuga on 2018/07/07.
// Copyright © 2018年 Hoge. All rights reserved.
//
import Foundation
private let formatter: NumberFormatter = NumberFormatter()
extension Int64 {
private func formattedString(style: NumberFormatter.Style,
localeIdentifier: String) -> String {
formatter.numberStyle = style
formatter.locale = Locale(identifier: localeIdentifier)
// hogehoge
return formatter.string(from: self as NSNumber) ?? ""
}
// JPYString converts the int64 to yen notation one like ¥1,000,000
// hoge
public var JPYString: String {
return formattedString(style: .currency, localeIdentifier: "ja_JP")
}
// formattedJPString converts the int64 to thousand separator string like 1,000,000
public func formattedJPString() -> String {
return formattedString(style: .decimal, localeIdentifier: "ja_JP")
}
}
上記の検索・置換ルールの実装は次の通り。
- Filter メソッドで .swift ファイルだけを対象にする
- Mapping メソッドで
//
を///
に置換した新しいコンテンツを生成する - Output メソッドでファイルを上書きする
package main
import (
"bytes"
"io/ioutil"
"strings"
)
func containsMulti(b []byte, subslices [][]byte) bool {
for _, subslice := range subslices {
if bytes.Contains(b, subslice) {
return true
}
}
return false
}
func mappingRecursive(n int, lines [][]byte) [][]byte {
if n < 0 {
return lines
}
beforeLine := lines[n]
if bytes.Contains(beforeLine, []byte("/// ")) {
return lines
}
if bytes.Contains(beforeLine, []byte("// ")) {
beforeLine = bytes.Replace(beforeLine, []byte("// "), []byte("/// "), 1)
}
if bytes.Equal(lines[n], beforeLine) {
return lines
}
lines[n] = beforeLine
return mappingRecursive(n-1, lines)
}
// Rule implements the Rewrite.Rule interface.
type Rule struct{}
// Filter filters the file using the filepath.
func (*Rule) Filter(filepath string) (bool, error) {
if !strings.HasSuffix(filepath, ".swift") {
return false, nil
}
return true, nil
}
// Mapping maps the file content with new one.
func (*Rule) Mapping(content []byte) ([]byte, bool, error) {
newLine := []byte("\n")
lines := bytes.Split(content, newLine)
for i, line := range lines {
if containsMulti(line, [][]byte{[]byte("func"), []byte("class"), []byte("var")}) {
lines = mappingRecursive(i-1, lines)
}
}
newContent := bytes.Join(lines, newLine)
return newContent, !bytes.Equal(content, newContent), nil
}
// Output writes the content to the file.
func (*Rule) Output(filepath string, content []byte) error {
// 補足: 新規作成されるわけではないので権限が変更されることはない
return ioutil.WriteFile(filepath, content, 0644)
}
main 関数では、実装した Rule 構造体を rewrite.Run に渡すだけ。
- dryrun オプションに true を渡すと Output メソッドの呼び出しがスキップされる
- https://godoc.org/github.com/yoheimuta/go-rewrite/rewrite
package main
import (
"flag"
"github.com/yoheimuta/go-rewrite/rewrite"
)
var (
root = flag.String("root", ".", "root path")
dryrun = flag.Bool("dryrun", true, "the flag whether to overwrite")
)
func main() {
flag.Parse()
rule := &Rule{}
rewrite.Run(*root, rule, rewrite.WithDryrun(*dryrun))
}
Author And Source
この問題について(複雑な条件のファイル検索・置換を楽にするライブラリ「go-rewrite」を作った), 我々は、より多くの情報をここで見つけました https://qiita.com/yoheimuta/items/479d0a5bd326a03e2336著者帰属:元の著者の情報は、元のURLに含まれています。著作権は原作者に属する。
Content is automatically searched and collected through network algorithms . If there is a violation . Please contact us . We will adjust (correct author information ,or delete content ) as soon as possible .