ค้นหาและแทนที่ด้วยนิพจน์ทั่วไป


26

ฉันมีไฟล์ที่มีค่าเริ่มต้นของผู้ใช้เป็นจำนวนมากฉันต้องการเปลี่ยนข้อความบางส่วน แต่ฉันกำลังต่อสู้กับผู้จับคู่และผู้แทนที่ ใช้ตัวอย่างต่อไปนี้:

###############################################################################
# Trackpad, mouse, keyboard, Bluetooth accessories, and input                 #
###############################################################################

# Trackpad: enable tap to click for this user and for the login screen
defaults write com.apple.driver.AppleBluetoothMultitouch.trackpad Clicking -bool true

ฉันต้องการแทนที่# Trackpad: ...ด้วยrunning "Trackpad: ..."

เมื่อหมดปัญหาฉันก็พบกับบางสิ่งที่ใช้เครื่องมือทดสอบ regex:

/\n\n#\s(.*)/g

ถ้าฉันลองและใช้สิ่งนี้เป็นกลุ่มมันไม่ได้ผลสำหรับฉัน:

:/\n\n#\s(.*)/running "\1"/g

ฉันเดาว่าปัญหาของฉันเดือดร้อนเหลือคำถามสองข้อ:

  1. ฉันจะหลีกเลี่ยงการค้นหา\nตัวอักษรและให้แน่ใจว่า#จะไม่ปรากฏในตอนท้ายของกลุ่มการค้นหา?
  2. ฉันจะใช้กลุ่มการดักจับได้อย่างมีประสิทธิภาพได้อย่างไร

มีคำตอบที่ดีบางข้อด้านล่าง ยากที่จะเลือกระหว่างทั้งสาม แต่ฉันรู้สึกว่าคำตอบที่เลือกนั้นแม่นยำที่สุดสำหรับสเป็คดั้งเดิมของฉัน ฉันแนะนำให้คุณลองคำตอบทั้งสามด้วยไฟล์จริงเพื่อดูว่าคุณรู้สึกอย่างไรกับพวกเขา

คำตอบ:


18

เพื่อให้ชัดเจน…ฉันเชื่อว่าคุณขอให้สิ่งนี้เป็นผลมาจากการทดแทนหรือไม่

###############################################################################
# Trackpad, mouse, keyboard, Bluetooth accessories, and input                 #
###############################################################################

running "Trackpad: enable tap to click for this user and for the login screen"
defaults write com.apple.driver.AppleBluetoothMultitouch.trackpad Clicking -bool true

ในกรณีนั้นฉันแนะนำคำสั่งต่อไปนี้:

:%s/\n\n#\s\+\(.*\)/^M^Mrunning "\1"/

คำอธิบายของรูปแบบ

:s/PATTERN/REPLACEMENT/เป็นคำสั่งทดแทน เปอร์เซ็นต์การลงชื่อเข้าใช้:%sทำให้มันทำงานได้ทั้งไฟล์ไม่ใช่แค่บรรทัดปัจจุบัน

\n\nบอกว่าสายของดอกเบี้ยที่จะต้องเกิดขึ้นหลังจากบรรทัดว่าง หากคุณไม่สนใจบรรทัดว่างก่อนหน้าก็^จะพอเพียง

#\s\+จับคู่อักขระแฮชแล้วตามด้วยอักขระเว้นวรรคหนึ่งตัวหรือมากกว่า \(.*\)จับข้อความที่ตามมาทั้งหมดในบรรทัด

คำอธิบายของข้อความการแทนที่

^M^Mแทรกสองบรรทัดเพื่อแทนที่ส่วน\n\nที่มีอยู่ในรูปแบบ มิฉะนั้นข้อความจะถูกย้ายไปที่ท้ายบรรทัดก่อนหน้าบรรทัดว่าง ในการพิมพ์แต่ละกด^MCtrl-V Ctrl-M

จากนั้นแทรกสตริงrunningตามด้วยสิ่งใดก็ตามที่ถูกจับในวงเล็บภายในเครื่องหมายคำพูดคู่


ฉันไม่สามารถแก้ไขคำตอบของคุณ แต่ฉันเชื่อว่าคุณหมายถึง:%s/\v\n\n#\s+(.*)/^M^Mrunning "\1"/(เพิ่มธง "magic") เป็นการยากที่จะเลือกคำตอบที่ถูกต้องสำหรับคำถามดั้งเดิมของฉัน แต่ฉันรู้สึกว่าคำตอบนี้เป็นคำตอบที่คาดหวังดั้งเดิมที่สุดของฉัน นอกจากนี้ยังเป็นไฟล์เดียวที่ใช้ได้ตลอดทั้งไฟล์โดยไม่จำเป็นต้องเลือกช่วง
squarefrog

1
IIRC คุณสามารถใช้\rแทนที่จะ^Mรับสายใหม่ได้หรือไม่
muru

11

ฉันจะใช้สิ่งที่ชอบ:

:s/^#\s\+\(.\{-}\):/running "\1":/
  • ^#เพื่อจับคู่#อักขระที่ยึดไว้ที่จุดเริ่มต้นของบรรทัด (ตอบคำถามนี้ 1)
  • \s\+ เพื่อจับคู่กับช่องว่างใด ๆ อย่างน้อยหนึ่งครั้ง
  • \( เพื่อเริ่มกลุ่ม (ตอบคำถามข้อ 2)
  • .\{-}\เพื่อให้ตรงกับตัวอักษรใด ๆ 0 ครั้งหรือมากกว่าในทางที่ไม่โลภ ; สิ่งนี้แตกต่างจากการ.*พยายามจับคู่ให้น้อยที่สุดและไม่มากเท่าที่เป็นไปได้ ลองเพิ่ม:ตัวละครในความคิดเห็นเพื่อดูว่าทำไมเรื่องนี้ถึงสำคัญ
  • \) เพื่อสิ้นสุดกลุ่มย่อย
  • : ตรงกับตัวอักษร :

จากนั้นเราจะแทนที่สิ่งนี้ด้วยข้อความที่คุณต้องการและใช้\1เพื่ออ้างอิงถึงกลุ่มที่เราจับภาพไว้

ฉันมากับสิ่งที่ใช้ทดสอบ regex

ไวยากรณ์ของนิพจน์ทั่วไปเหมือนกับไวยากรณ์ของ wiki: มีกลุ่มของพวกเขาพวกเขาทั้งหมดดูเหมือนอย่างรวดเร็วไม่มีใครดีกว่าคนอื่นอย่างเห็นได้ชัด แต่มีความแตกต่างมากมาย

วันนี้นิพจน์ปกติ "Perl Compatible" ที่เรียกว่าเป็นค่าเริ่มต้น de-facto ในภาษาส่วนใหญ่ แต่การแสดงออกปกติ Vim ไม่เข้ากันได้กับการแสดงออกที่เข้ากันได้กับ Perl! Vim regexp syntax กลับไปอย่างน้อยยุค 70 เมื่อ Perl ไม่ได้อยู่ใกล้ ๆ

คุณสามารถเห็นสิ่งนี้ได้ด้วยกลุ่มย่อยที่คุณต้องใช้\(และไม่ใช้((ซึ่งเข้ากันได้กับไวยากรณ์ 'พื้นฐาน' ของ POSIX แต่ไม่สามารถใช้ร่วมกับไวยากรณ์เพิ่มเติมหรือ POSIX ของ POSIX ที่พบได้ทั่วไป คุณสามารถควบคุมสิ่งนี้ได้โดยการเพิ่ม\vธงในรูปแบบ (ดู:help /\vรายละเอียด) สิ่งนี้จะทำให้มัน "เข้ากันได้มากกว่า" แต่ไม่สมบูรณ์ (คุณยังต้องใช้.{-}สำหรับการแข่งขันที่ไม่ใช่โลภ)

ดังนั้นสิ่งนี้อาจอธิบายได้ว่าทำไมการใช้ "เครื่องมือทดสอบ regex" เกือบจะใช้งานได้ แต่ไม่มากนัก

http://www.vimregex.com/เป็นภาพรวมที่ดี / cheatsheet ของ Vim regexps


1
คำตอบที่ดีโดยเฉพาะอย่างยิ่งกับทำไม regex! = regex :ในฐานะที่เป็นสำหรับการค้นหาของคุณและแทนที่มันเป็นบิตหากินเป็นความคิดเห็นอาจหรือไม่อาจมี ดูไฟล์ฉบับเต็มเพื่อดูรายละเอียดgithub.com/squarefrog/dotfiles/blob/master/osx/…
squarefrog

8

คุณสามารถ:

  • ค้นหาบรรทัดที่ไม่สิ้นสุด#::/[^#]$/
  • แทนที่#\s(.*)ที่จุดเริ่มต้นของบรรทัด:s/\v^#\s(.*)/running "\1"/

ในการใช้กลุ่มคุณต้อง:

  • หลบหนีวงเล็บเพื่อให้พวกเขากลายเป็นส่วนหนึ่งของไวยากรณ์ regex: \(.*\)หรือ
  • ใช้"magic"โดยเริ่มต้นการแสดงออกด้วย\v:s/\v...

รวมมันเข้าด้วยกัน:

:/[^#]$/s/\v^#\s(.*)/running "\1"/

1
สิ่งนี้ใช้ได้ผลดีเยี่ยมขอบคุณที่รวมคำอธิบายของแท็กที่มีความหมายมากกว่าข้อความที่ฉันต้องการเขียน
squarefrog

ขอบคุณที่ทำให้มันชัดเจนว่าจะต้องหลบหนีวงเล็บเพื่อที่จะกลายเป็นส่วนหนึ่งของไวยากรณ์ regex ฉันมักจะมีปัญหากับกลุ่ม regex ในการทำให้พวกเขาทำงาน
Adama
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.