ตรวจสอบว่าสตริงมีองค์ประกอบจากรายการ (ของสตริง)


155

สำหรับบล็อกของรหัสต่อไปนี้:

For I = 0 To listOfStrings.Count - 1
    If myString.Contains(lstOfStrings.Item(I)) Then
        Return True
    End If
Next
Return False

ผลลัพธ์คือ:

กรณีที่ 1:

myString: C:\Files\myfile.doc
listOfString: C:\Files\, C:\Files2\
Result: True

กรณีที่ 2:

myString: C:\Files3\myfile.doc
listOfString: C:\Files\, C:\Files2\
Result: False

รายการ (listOfStrings) อาจมีหลายรายการ (ขั้นต่ำ 20) และจะต้องมีการตรวจสอบกับหลายพันสาย (เช่น myString)

มีวิธีที่ดีกว่า (มีประสิทธิภาพมากขึ้น) ในการเขียนรหัสนี้หรือไม่?

คำตอบ:


359

ด้วย LINQ และการใช้ C # (ฉันไม่ทราบว่ามาก VB วันนี้):

bool b = listOfStrings.Any(s=>myString.Contains(s));

หรือ (สั้นกว่าและมีประสิทธิภาพมากกว่า แต่ชัดเจนน้อยกว่า):

bool b = listOfStrings.Any(myString.Contains);

หากคุณกำลังทดสอบความเท่าเทียมกันมันจะคุ้มค่าที่จะดูHashSetฯลฯ แต่สิ่งนี้จะไม่ช่วยในการจับคู่บางส่วนยกเว้นว่าคุณแยกเป็นชิ้นส่วนและเพิ่มลำดับความซับซ้อน


update: ถ้าคุณหมายถึง "StartsWith" จริงๆคุณสามารถเรียงลำดับรายการและวางลงในอาร์เรย์ จากนั้นใช้Array.BinarySearchเพื่อค้นหาแต่ละรายการ - ตรวจสอบโดยการค้นหาเพื่อดูว่าตรงกันหรือไม่ทั้งหมด


1
แทนที่จะมีฉันจะใช้ StartsWith ตามตัวอย่างของเขา
tvanfosson

@tvanfosson - ขึ้นอยู่กับว่าตัวอย่างนั้นครอบคลุมครบถ้วนหรือไม่ แต่ใช่ฉันเห็นด้วย ง่ายต่อการเปลี่ยนแปลงแน่นอน
Marc Gravell

รหัสนี้มีประสิทธิภาพมากขึ้นเพียงใดในระดับอัลกอริทึม มันสั้นและเร็วขึ้นหากการวนซ้ำใน "ใด ๆ " เร็วขึ้น แต่ปัญหาที่คุณต้องทำการจับคู่ที่ตรงกันหลายครั้งเหมือนกัน
Torsten Marek

คุณสามารถตั้งค่าเครื่องมือเปรียบเทียบแบบกำหนดเองได้หากคุณใช้ชุด
Fortyrunner

ข้อที่สองนั้นไม่ได้มีประสิทธิภาพมากนักโดยความแตกต่างที่วัดได้ในทางปฏิบัติ
ICR

7

เมื่อคุณสร้างสตริงของคุณมันควรจะเป็นแบบนี้

bool inact = new string[] { "SUSPENDARE", "DIZOLVARE" }.Any(s=>stare.Contains(s));

5

มีข้อเสนอแนะจำนวนมากจากคำถามที่คล้ายกันก่อนหน้านี้ " วิธีที่ดีที่สุดในการทดสอบสตริงที่มีอยู่กับรายการเปรียบเทียบขนาดใหญ่ "

Regex อาจเพียงพอสำหรับความต้องการของคุณ นิพจน์จะเป็นการรวมกันของสตริงย่อยของผู้สมัครทั้งหมดโดยมี OR "| " ผู้ประกอบระหว่างพวกเขา แน่นอนว่าคุณจะต้องระวังตัวอักขระที่ไม่ใช้ค่า Escape เมื่อสร้างนิพจน์หรือความล้มเหลวในการรวบรวมเพราะความซับซ้อนหรือข้อ จำกัด ด้านขนาด

อีกวิธีหนึ่งในการทำเช่นนี้คือการสร้างโครงสร้างข้อมูล trieเพื่อแสดงสตริงย่อยของผู้สมัครทั้งหมด (ซึ่งอาจซ้ำซ้อนกับสิ่งที่ regex matcher กำลังทำอยู่) ในขณะที่คุณก้าวผ่านตัวละครแต่ละตัวในชุดทดสอบคุณจะต้องสร้างตัวชี้ใหม่ไปที่รูทของคู่และเลื่อนตัวชี้ที่มีอยู่ไปยังลูกที่เหมาะสม (ถ้ามี) คุณได้รับการแข่งขันเมื่อตัวชี้ใด ๆ ถึงใบไม้


5

ฉันชอบคำตอบของ Marc แต่ต้องการให้การจับคู่ประกอบด้วยเป็น CaSe InSenSiTiVe

นี่คือทางออก:

bool b = listOfStrings.Any(s => myString.IndexOf(s, StringComparison.OrdinalIgnoreCase) >= 0))

ไม่ควร> -1
CSharped

1
@CSharped ไม่สำคัญว่า> -1 (มากกว่าลบ 1) และ> = 0 (มากกว่าหรือเท่ากับศูนย์) เป็นสิ่งเดียวกัน
WhoIsRich

2

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

นอกจากนี้ตามรูปแบบของคุณดูเหมือนว่าคุณสามารถแยกส่วนแรกของเส้นทางสำหรับ myString แล้วย้อนกลับการเปรียบเทียบ - มองหาเส้นทางเริ่มต้นของ myString ในรายการสตริงแทนที่จะเป็นทางอื่น ๆ

string[] pathComponents = myString.Split( Path.DirectorySeparatorChar );
string startPath = pathComponents[0] + Path.DirectorySeparatorChar;

return listOfStrings.Contains( startPath );

แก้ไข : นี่จะเร็วยิ่งขึ้นหากใช้ HashSet idea @Marc Gravell กล่าวถึงเนื่องจากคุณสามารถเปลี่ยนContainsเป็นContainsKeyและการค้นหาจะเป็น O (1) แทน O (N) คุณจะต้องตรวจสอบให้แน่ใจว่าเส้นทางนั้นตรงกันทุกประการ โปรดทราบว่านี่ไม่ใช่วิธีแก้ปัญหาทั่วไปเช่นเดียวกับ @Marc Gravell แต่เหมาะสำหรับตัวอย่างของคุณ

ขออภัยสำหรับตัวอย่าง C # ฉันมีกาแฟไม่พอที่จะแปลเป็น VB


เริ่มใหม่ด้วย อาจจะเรียงลำดับล่วงหน้าและใช้การค้นหาแบบไบนารีหรือไม่ นั่นอาจจะเร็วขึ้นอีกครั้ง
Marc Gravell

2

คำถามเก่า ๆ แต่เนื่องจากVB.NETเป็นข้อกำหนดดั้งเดิม ใช้ค่าเดียวกันของคำตอบที่ยอมรับ:

listOfStrings.Any(Function(s) myString.Contains(s))

1

คุณทดสอบความเร็วแล้วหรือยัง

นั่นคือคุณได้สร้างชุดตัวอย่างของข้อมูลและทำโปรไฟล์แล้ว มันอาจจะไม่เลวร้ายอย่างที่คุณคิด

นี่อาจเป็นสิ่งที่คุณสามารถวางไข่ในเธรดแยกต่างหากและให้ภาพลวงตาของความเร็ว!


0

หากความเร็วเป็นสิ่งสำคัญคุณอาจต้องการค้นหาอัลกอริทึม Aho-Corasickสำหรับชุดรูปแบบ

มันเป็นคู่ชีวิตที่มีลิงค์เชื่อมโยงที่ล้มเหลวนั่นคือความซับซ้อนคือ O (n + m + k) โดยที่ n คือความยาวของข้อความที่ป้อนเข้า, m คือความยาวสะสมของรูปแบบและ k จำนวนการจับคู่ คุณเพียงแค่ต้องปรับเปลี่ยนอัลกอริทึมเพื่อยกเลิกหลังจากพบคู่แรก



0

ข้อเสียเปรียบของContainsวิธีการคือไม่อนุญาตให้ระบุประเภทการเปรียบเทียบซึ่งมักมีความสำคัญเมื่อเปรียบเทียบสตริง มันไวต่อวัฒนธรรมและตรงตามตัวพิมพ์ใหญ่เสมอ ดังนั้นฉันคิดว่าคำตอบของ WhoIsRich นั้นมีค่าฉันแค่ต้องการแสดงทางเลือกที่ง่ายกว่า:

listOfStrings.Any(s => s.Equals(myString, StringComparison.OrdinalIgnoreCase))
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.