Ruby ลบอักขระ N ตัวสุดท้ายออกจากสตริงหรือไม่


263

วิธีที่แนะนำในการลบnอักขระตัวสุดท้ายออกจากสตริงคืออะไร?


ถ้าคุณรู้คำต่อท้าย (คำตอบที่ดีที่สุดแนะนำให้มาที่นี่เพื่อหาสิ่งนี้) คุณสามารถใช้delete_suffixจาก Ruby 2.5 ข้อมูลเพิ่มเติมที่นี่
SRack

คำตอบ:


36

ทับทิม 2.5+

ในฐานะของ Ruby 2.5 คุณสามารถใช้delete_suffixหรือdelete_suffix!เพื่อให้บรรลุสิ่งนี้ในลักษณะที่รวดเร็วและอ่านได้

เอกสารเกี่ยวกับวิธีการที่มีที่นี่

หากคุณรู้ว่าคำต่อท้ายคืออะไรนี่เป็นสำนวน (และฉันขอโต้แย้งยิ่งอ่านง่ายกว่าคำตอบอื่น ๆ ที่นี่):

'abc123'.delete_suffix('123')     # => "abc"
'abc123'.delete_suffix!('123')    # => "abc"

มันเร็วยิ่งขึ้นอย่างมาก (เกือบ40%ด้วยวิธีปัง) กว่าคำตอบยอดนิยม นี่คือผลลัพธ์ของการวัดมาตรฐานเดียวกัน:

                     user     system      total        real
chomp            0.949823   0.001025   0.950848 (  0.951941)
range            1.874237   0.001472   1.875709 (  1.876820)
delete_suffix    0.721699   0.000945   0.722644 (  0.723410)
delete_suffix!   0.650042   0.000714   0.650756 (  0.651332)

ฉันหวังว่าสิ่งนี้มีประโยชน์ - โปรดทราบว่าวิธีการนี้ไม่ได้รับการยอมรับจาก regex ดังนั้นหากคุณไม่ทราบว่าคำต่อท้ายจะไม่สามารถใช้งานได้ในขณะนี้ อย่างไรก็ตามในขณะที่คำตอบที่ยอมรับ ( อัปเดต: ณ เวลาที่เขียน ) กำหนดไว้เหมือนกันฉันคิดว่านี่อาจเป็นประโยชน์กับบางคน


1
@JPSilvashy ฉันไม่เคยยินดีที่จะทำเครื่องหมายถูกอีกต่อไป นี่คือคำตอบที่ดี
Wayne Conrad

1
นี่เป็นฟังก์ชั่นที่ยอดเยี่ยมและรวดเร็ว แต่นี่ไม่ใช่คำตอบที่ถูกต้องเพราะมันไม่ได้ตอบคำถามที่แน่นอนWhat is the preferred way of removing the last n characters from a string?
Sam

1
ขอบคุณสำหรับความคิดเห็น @Sam - คำถามนี้มีคำตอบเป็นเวลาเกือบเก้าปีที่ทำงานเช่นเดียวกับที่มี 261 upvotes ในขณะที่เขียน JP ยอมรับและเพิ่งเปลี่ยนมาใช้สิ่งนี้ดังนั้นฉันจึงบอกว่ามันตอบคำถามของพวกเขา :) ฉันคิดว่าฉันนำเสนอสิ่งนี้ในทางเลือกที่ทันสมัยและหวังว่ามันจะช่วยผู้คนที่มาที่นี่ ในความเป็นจริงฉันได้ครอบคลุมทั้งหมดนี้ในคำถาม - ฉันหวังว่าวิธีนี้จะยอมรับ regex เร็ว ๆ นี้แม้ว่าจะมีทางเลือกที่สมบูรณ์แบบสำหรับกรณีของคุณด้านล่างนี้
SRack

316
irb> 'now is the time'[0...-4]
=> "now is the "

9
โปรดทราบว่าใช้งานได้ใน Ruby 1.9 เท่านั้น ในทับทิม 1.8 นี้จะลบสุดท้ายไบต์ไม่สุดท้ายตัวละคร
Jörg W Mittag

2
แต่เขาอาจหมายถึง"bytes"ถ้าเขาใช้ 1.8.x
DigitalRoss

4
วิธีนี้ใช้ได้ผล แต่ฉันกำลังค้นหา 'abc123'.chomp (' 123 ') ให้เร็วเป็นสองเท่าสำหรับทั้งสตริงสั้นและยาวมาก (Ruby 2.0.0-p247) ทุกคนสามารถยืนยันได้ไหม
Plasmarob

10
สำหรับผู้ที่สงสัยว่าทำไมตัวอย่างเฉพาะนี้ใช้งานไม่ได้มี 3 จุดไม่ใช่ 2 คือ[0...-4]ไม่ใช่[0..-4]
rorofromfrance

2
@Plamarob ฉันคิดว่ามันมีความเกี่ยวข้องมากกว่าที่จะบอกว่า chomp เร็วกว่า 53 nanoseconds มากกว่าที่จะบอกว่ามันเร็วกว่าสองเท่า จากนั้นคุณสามารถประมาณราคากับมูลค่าการใช้งานได้ดีขึ้นและอาจเป็นสิ่งที่ควรปรับปรุงในสถานการณ์ที่กำหนดหรือไม่
cesoid

266

หากตัวละครที่คุณต้องการลบเป็นตัวละครเดียวกันเสมอให้พิจารณาchomp:

'abc123'.chomp('123')    # => "abc"

ข้อดีของchompคือ: ไม่นับและรหัสจะสื่อสารสิ่งที่กำลังทำอย่างชัดเจนยิ่งขึ้น

หากไม่มีข้อโต้แย้งให้chompลบการลงท้ายบรรทัด DOS หรือ Unix หากมีอยู่:

"abc\n".chomp      # => "abc"
"abc\r\n".chomp    # => "abc"

จากความคิดเห็นมีคำถามเกี่ยวกับความเร็วในการใช้งาน#chompเมื่อเทียบกับการใช้ช่วง นี่คือมาตรฐานเปรียบเทียบทั้งสอง:

require 'benchmark'

S = 'asdfghjkl'
SL = S.length
T = 10_000
A = 1_000.times.map { |n| "#{n}#{S}" }

GC.disable

Benchmark.bmbm do |x|
  x.report('chomp') { T.times { A.each { |s| s.chomp(S) } } }
  x.report('range') { T.times { A.each { |s| s[0...-SL] } } }
end

ผลการทดสอบเกณฑ์มาตรฐาน (โดยใช้ CRuby 2.13p242):

Rehearsal -----------------------------------------
chomp   1.540000   0.040000   1.580000 (  1.587908)
range   1.810000   0.200000   2.010000 (  2.011846)
-------------------------------- total: 3.590000sec

            user     system      total        real
chomp   1.550000   0.070000   1.620000 (  1.610362)
range   1.970000   0.170000   2.140000 (  2.146682)

ดังนั้น chomp จึงเร็วกว่าการใช้ช่วงโดย ~ 22%


5
สับก็เป็นตัวเลือก มันยุ่งน้อยลงเกี่ยวกับสิ่งที่ถูกลบ
Andrew Grimm

1
ฉันพบว่าสิ่งที่ตรงกันข้ามนั้นเป็นเรื่องจริง ความจริงก็คือ. chomp ดูเหมือนจะเร็วเป็นสองเท่า
Plasmarob

3
@Plamarob มาตรฐานในทับทิมมักจะแปลกใจ ฉันมักจะผิดที่ฉันไม่พยายามที่จะคาดเดาสิ่งที่จะเร็วขึ้น นอกจากนี้หากคำตอบจะได้รับการปรับปรุงจากผลการวัดประสิทธิภาพโปรดอย่าลังเลที่จะแก้ไข
Wayne Conrad

1
ถ้าฉันอ่านรหัสถูกต้องช่วงจะใช้เวลานานกว่า 53 วินาทีในการ chomp อาจเป็นการลากครั้งสำคัญในบางสถานการณ์ แต่การคำนึงถึงความเร็วที่แน่นอน (แทนที่จะเป็นเพียงความสัมพันธ์) อาจบอกคุณได้ว่าคุณควรจะผ่าน codebase ของคุณหรือไม่และเปลี่ยนช่วงทั้งหมดของคุณเป็น chomps (ถ้ามี) อาจจะไม่.
cesoid

1
ใช้งานได้ดี และคุณสามารถใช้chomp!()เพื่อดำเนินการกับ String แบบแทนที่
Joshua Pinter

57
str = str[0...-n]

5
โปรดทราบว่าใช้งานได้ใน Ruby 1.9 เท่านั้น ในทับทิม 1.8 นี้จะลบสุดท้ายไบต์ไม่สุดท้ายตัวละคร
Jörg W Mittag

5
นี่ดีกว่าคำตอบที่เลือกเนื่องจาก 3 จุด (... ) ง่ายต่อการจดจำเนื่องจาก -n หมายถึงนำอักขระ n ตัวออกจากจุดสิ้นสุดของสตริง
lulalala

ยัง "abcd" [0 ..- 2] # => "abc" ในขณะที่ "abcd" [0 ...- 2] # => "ab" ในความคิดของฉันตัวเลือกช่วง 3 จุดจะทำให้เกิดรหัสอธิบายตนเองมากขึ้น
mokagio

31

chopผมจะแนะนำ ฉันคิดว่ามันถูกกล่าวถึงในความคิดเห็น แต่ไม่มีลิงก์หรือคำอธิบายดังนั้นนี่คือเหตุผลที่ฉันคิดว่ามันจะดีกว่า:

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

หากคุณต้องการลบตัวละครมากกว่าหนึ่งตัวchompนั่นเป็นทางออกที่ดีที่สุดของคุณ นี่คือสิ่งที่เอกสารทับทิมพูดถึงchop:

ส่งคืนสตริงใหม่โดยลบอักขระตัวสุดท้ายออก หากสตริงลงท้ายด้วย \ r \ n อักขระทั้งสองตัวจะถูกลบออก การนำ chop ไปใช้กับสตริงว่างจะส่งคืนสตริงว่าง String # chomp มักจะเป็นทางเลือกที่ปลอดภัยกว่าเนื่องจากสตริงจะไม่เปลี่ยนแปลงหากไม่ได้อยู่ในตัวคั่นเร็กคอร์ด

แม้ว่าส่วนใหญ่จะใช้ในการลบตัวคั่นเช่น\r\nฉันใช้เพื่อลบอักขระตัวสุดท้ายออกจากสตริงอย่างง่ายตัวอย่างเช่นการsสร้างคำเอกพจน์


1
นั่นจะไม่ลบอักขระตัวสุดท้ายตัวเดียวออกใช่ไหม คำถามเกี่ยวกับ "ตัวละคร" ในพหูพจน์
maetthew

1
ใช่คุณพูดถูก แต่ chomp ('chars') จะลบ 'chars' สุดท้ายออก มันไม่ชัดเจนสำหรับฉันถ้า OP ต้องการตัวอักษรเฉพาะหรือแค่ N ตัวอักษร
kakubei

ใช่นี่คือคำตอบเฉพาะเมื่อคุณต้องการลบตัวละครเดียวเท่านั้น (ซึ่งเป็นกรณีของฉันด้วย)
Quv

29
name = "my text"
x.times do name.chop! end

ที่นี่ในคอนโซล:

>name = "Nabucodonosor"
 => "Nabucodonosor" 
> 7.times do name.chop! end
 => 7 
> name
 => "Nabuco" 

มันมีประสิทธิภาพไหม? ดูเหมือนว่ามีค่าเทียบกับมาตรฐานเปรียบเทียบกับคำตอบอื่น ๆ :)
4615

16

การดร็อปnอักขระตัวสุดท้ายเหมือนกับการเก็บlength - nอักขระตัวแรก

การสนับสนุนที่แอ็คทีฟประกอบด้วยString#firstและString#lastวิธีการที่ให้วิธีที่สะดวกในการเก็บหรือวางnอักขระตัวแรก / ตัวสุดท้าย

require 'active_support/core_ext/string/access'

"foobarbaz".first(3)  # => "foo"
"foobarbaz".first(-3) # => "foobar"
"foobarbaz".last(3)   # => "baz"
"foobarbaz".last(-3)  # => "barbaz"

7

หากคุณใช้รางให้ลอง:

"my_string".last(2) # => "ng"

[แก้ไข]

วิธีรับสตริงโดยไม่มี 2 ตัวอักษรสุดท้าย:

n = "my_string".size
"my_string"[0..n-3] # => "my_stri"

หมายเหตุ: อักขระตัวสุดท้ายของสตริงอยู่ที่ n-1 ดังนั้นในการลบ 2 ครั้งสุดท้ายเราใช้ n-3


2
สิ่งนี้ไม่ได้ลบตัวอักษรสองตัวสุดท้าย มันส่งคืนพวกเขา
JP Silvashy

1
คุณไม่จำเป็นต้องวัดสตริงก่อนทับทิมใช้ดัชนีลบจากจุดสิ้นสุดโดยกำเนิด
Devon Parsons

3

คุณสามารถใช้สิ่งที่ชอบ

 "string".sub!(/.{X}$/,'')

ที่ไหนXเป็นจำนวนตัวอักษรที่จะเอา

หรือด้วยการกำหนด / ใช้ผล:

myvar = "string"[0..-X]

โดยที่Xจำนวนอักขระบวกหนึ่งตัวที่จะลบ



1

หากคุณตกลงกับการสร้างวิธีการเรียนและต้องการให้ตัวละครที่คุณตัดออกมาลองทำสิ่งนี้:

class String
  def chop_multiple(amount)
    amount.times.inject([self, '']){ |(s, r)| [s.chop, r.prepend(s[-1])] }
  end
end

hello, world = "hello world".chop_multiple 5
hello #=> 'hello '
world #=> 'world'


0

หากตัวละครที่คุณต้องการลบเป็นตัวละครเดียวกันเสมอให้พิจารณา chomp:

'abc123' chomp ('123')


-3
x = "my_test"
last_char = x.split('').last

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