อัลกอริทึม strcasecmp มีข้อบกพร่องหรือไม่


34

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

จาก man strcmp

ฟังก์ชั่น strcmp () เปรียบเทียบสองสตริง s1 และ s2 โลแคลไม่ถูกนำมาพิจารณา (สำหรับการเปรียบเทียบที่รับรู้โลแคลโปรดดู strcoll (3)) มันจะคืนค่าจำนวนเต็มน้อยกว่าเท่ากับหรือมากกว่าศูนย์ถ้าพบ s1 ตามลำดับจะน้อยกว่าเพื่อจับคู่หรือมากกว่า s2

จาก man strcasecmp

strcasecmp () ฟังก์ชั่นทำการเปรียบเทียบไบต์โดยไบต์ของสตริง s1 และ s2 โดยไม่สนใจกรณีของตัวละคร มันจะคืนค่าจำนวนเต็มน้อยกว่าเท่ากับหรือมากกว่าศูนย์ถ้าพบ s1 ตามลำดับจะน้อยกว่าเพื่อจับคู่หรือมากกว่า s2

int strcmp(const char *s1, const char *s2);
int strcasecmp(const char *s1, const char *s2);

รับข้อมูลนี้ฉันไม่เข้าใจผลลัพธ์ของรหัสต่อไปนี้:

#include <stdio.h>
#include <string.h>

int main()
{
    // ASCII values
    // 'A' = 65
    // '_' = 95
    // 'a' = 97

    printf("%i\n", strcmp("A", "_"));
    printf("%i\n", strcmp("a", "_"));
    printf("%i\n", strcasecmp("A", "_"));
    printf("%i\n", strcasecmp("a", "_"));
    return 0;
}

ouput:

-1  # "A" is less than "_"
1   # "a" is more than "_"
2   # "A" is more than "_" with strcasecmp ???
2   # "a" is more than "_" with strcasecmp

ปรากฏว่าถ้าตัวละครในปัจจุบันs1เป็นตัวอักษรมันจะถูกแปลงเป็นตัวพิมพ์เล็กเสมอโดยไม่คำนึงว่าตัวละครในปัจจุบันs2เป็นตัวอักษรหรือไม่

มีคนอธิบายพฤติกรรมนี้ได้ไหม บรรทัดแรกและบรรทัดที่สามไม่ควรเหมือนกันหรือ

ขอบคุณล่วงหน้า!

PS:
ฉันกำลังใช้gcc 9.2.0กับ Manjaro
นอกจากนี้เมื่อฉันรวบรวมกับ-fno-builtinธงฉันได้รับแทน:

-30
2
2
2

ฉันเดาว่าเป็นเพราะโปรแกรมไม่ได้ใช้ฟังก์ชั่นที่ดีที่สุดของ gcc แต่คำถามยังคงอยู่


2
เพิ่มกรณีทดสอบอื่นให้กับชุดของคุณ: printf("%i\n", strcasecmp("a", "_"));สิ่งนี้น่าจะมีผลเช่นเดียวกับprintf("%i\n", strcasecmp("A", "_"));แต่นั่นหมายความว่าหนึ่งในสองสายที่ไม่สนใจตัวพิมพ์เล็กและตัวพิมพ์ใหญ่นั้นจะไม่เห็นด้วยกับตัวพิมพ์เล็กและตัวพิมพ์ใหญ่
anton.burger

ดูเหมือนว่าคำอธิบายของstrcasecmpคุณอ้างถึงไม่ถูกต้อง รายละเอียดเพิ่มเติมในคำตอบ upvoted
Jabberwocky

9
มันเป็นสิ่งเดียวที่สมเหตุสมผล ฟังก์ชั่นที่บอกว่าA < _ && a > _ && A == aจะทำให้เกิดปัญหามากมาย
ikegami

นอกเหนือ: "ผมพยายามที่จะ reimplement ฟังก์ชั่น strcasecmp ใน C" -> แม้ว่ารหัสไม่ได้แสดงให้แน่ใจว่าจะเทียบได้กับ unsigned char"ราวกับว่า" C17 / 18 "การจัดการสตริง <string.h>" -> "สำหรับฟังก์ชั่นทั้งหมดใน subclause นี้อักขระแต่ละตัวจะถูกตีความราวกับว่ามันมีประเภทunsigned char" สิ่งนี้สร้างความแตกต่างเมื่อcharค่าอยู่นอกช่วง ASCII 0-127
chux - Reinstate Monica

1
ในความแตกต่างของเอาต์พุตที่มีบิวด์อินและไม่มี: ทั้งคู่พูดเหมือนกันเนื่องจากผลลัพธ์ของมันนั้นเหมือนกัน <0 และ> 0 และคุณไม่มีตัวอย่างสำหรับ == 0 แต่คุณสามารถเห็นอัลกอริทึมส่องผ่าน: ค่าที่ส่งคืนบางส่วนเป็นความแตกต่างของอักขระที่ไม่เท่ากันตัวแรก
busybee

คำตอบ:


31

พฤติกรรมนั้นถูกต้อง

ตามข้อกำหนดPOSIXstr\[n\]casecmp() :

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

ที่ยังเป็นส่วนหนึ่งของหมายเหตุส่วนของหน้าคนลินุกซ์ :

มาตรฐาน POSIX.1-2008 พูดถึงฟังก์ชั่นเหล่านี้:

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

ทำไม?

@HansOlsson ชี้ให้เห็นในคำตอบของเขาทำการเปรียบเทียบแบบตัวพิมพ์เล็กและตัวพิมพ์ใหญ่ระหว่างตัวอักษรเพียงตัวเดียวและให้การเปรียบเทียบอื่น ๆ ทั้งหมดเพื่อให้ได้ผลลัพธ์ที่ "เป็นธรรมชาติ" อย่างที่ทำในstrcmp()การเรียงลำดับ

หาก'A' == 'a'(คำจำกัดความของการเปรียบเทียบแบบตัวพิมพ์ใหญ่และตัวพิมพ์เล็ก) ดังนั้น'_' > 'A'และ'_' < 'a'("ธรรมชาติ" ส่งผลให้ชุดอักขระ ASCII) ไม่สามารถเป็นจริงได้ทั้งคู่


การทำเช่นการเปรียบเทียบกรณีตายระหว่างตัวอักษรเท่านั้นจะไม่ส่งผล'_' > 'A' && '_' < 'a'; ดูเหมือนจะไม่เป็นตัวอย่างที่ดีที่สุด
ดาวเคราะห์น้อยที่มีปีก

1
@AsteroidsWithWings นี่คือตัวอักษรที่ใช้ในคำถาม และถ้า'a' == 'A' ตามคำนิยามถ้าคุณทำการเปรียบเทียบระหว่างค่า "ธรรมชาติ" ของ'a', 'A'และ'_'คุณไม่สามารถทำการเปรียบเทียบแบบตัวพิมพ์เล็กและตัวพิมพ์ใหญ่ระหว่าง'A'และ'a'เพื่อให้ได้ความเท่าเทียมกันและได้ผลลัพธ์การเรียงที่สอดคล้องกัน
Andrew Henle

ฉันไม่ได้โต้แย้ง แต่ตัวอย่างเคาน์เตอร์ที่คุณระบุนั้นดูเหมือนจะไม่เกี่ยวข้องกัน
Asteroids With Wings

@AsteroidsWithWings ผ่านการออกกำลังกายทางจิตของการสร้างต้นไม้ไบนารีจาก'a', 'A'และ'_', จะผ่านทั้งหมด 6 คำสั่งของการแทรกเข้าไปในต้นไม้และเปรียบเทียบผลจากการตามที่ระบุไว้ "ตัวอักษรตัวพิมพ์เล็กเสมอ" กับคำถามที่เสนอ "แปลงตัวอักษรเท่านั้น เมื่อเป็นการเปรียบเทียบแบบตัวอักษรกับตัวอักษร " ตัวอย่างเช่นการใช้อัลกอริทึมหลังและเริ่มต้นด้วย'_', 'a'และ'A'ลมขึ้นในด้านตรงข้ามของต้นไม้ แต่พวกเขากำลังที่กำหนดไว้ที่เท่าเทียมกัน อัลกอริธึม "แปลงตัวอักษรเป็นตัวพิมพ์เล็กเป็นตัวอักษรตัวเล็ก" เสียและอัลกอริธึมทั้งสามแสดงว่า
Andrew Henle

เอาล่ะฉันขอแนะนำให้แสดงให้เห็นว่าในคำตอบเพราะในขณะนี้มันก็กระโดดไปชี้ให้เห็นว่า" '_' > 'A' และ'_' < 'a'ไม่สามารถเป็นจริงทั้งสอง"โดยไม่บอกเราว่าทำไมเราควรคิดว่ามันจะเป็น (นั่นเป็นภารกิจสำหรับผู้ตอบไม่ใช่ไม่ใช่หนึ่งในผู้อ่านหลายล้านคน)
Asteroids With Wings

21

ลิงก์อื่น ๆhttp://man7.org/linux/man-pages/man3/strcasecmp.3p.html สำหรับ strcasecmp กล่าวว่าการแปลงเป็นตัวพิมพ์เล็กเป็นพฤติกรรมที่ถูกต้อง (อย่างน้อยในโลแคล POSIX)

เหตุผลสำหรับพฤติกรรมนั้นคือถ้าคุณใช้ strcasecmp เพื่อเรียงลำดับของสตริงคุณจำเป็นต้องได้ผลลัพธ์ที่สมเหตุสมผล

มิฉะนั้นหากคุณพยายามเรียงลำดับ "A", "C", "_", "b" โดยใช้เช่น qsort ผลลัพธ์จะขึ้นอยู่กับลำดับการเปรียบเทียบ


3
มิฉะนั้นหากคุณพยายามเรียงลำดับ "A", "C", "_", "b" โดยใช้เช่น qsort ผลลัพธ์จะขึ้นอยู่กับลำดับการเปรียบเทียบ จุดดี. เป็นไปได้ว่าเหตุผลที่ POSIX ระบุพฤติกรรม
Andrew Henle

6
ยิ่งเป็นรูปธรรมคุณต้องมีคำสั่งซื้อทั้งหมดสำหรับการเรียงลำดับซึ่งจะไม่เป็นกรณีนี้หากคุณกำหนดการเปรียบเทียบดังเช่นในคำถาม (เนื่องจากไม่มีการถ่ายทอด)
Dukeling

8

ปรากฏว่าหากอักขระปัจจุบันใน s1 เป็นตัวอักษรมันจะถูกแปลงเป็นตัวพิมพ์เล็กเสมอโดยไม่คำนึงว่าอักขระปัจจุบันใน s2 เป็นตัวอักษรหรือไม่

ถูกต้อง - และมันคือสิ่งที่strcasecmp()ฟังก์ชั่นควรทำ! มันเป็นPOSIXฟังก์ชั่นแทนที่จะเป็นส่วนหนึ่งของCมาตรฐาน แต่จาก " The Open Group Base Specifications, Issue 6 ":

ในโลแคล POSIX strcasecmp () และ strncasecmp () จะทำตัวราวกับว่าสตริงนั้นถูกแปลงเป็นตัวพิมพ์เล็กแล้วจึงทำการเปรียบเทียบแบบไบต์ ผลลัพธ์ไม่ได้ระบุในที่อื่น

อนึ่งพฤติกรรมนี้ยังเกี่ยวข้องกับ_stricmp()ฟังก์ชัน (ตามที่ใช้ใน Visual Studio / MSCV):

ฟังก์ชัน _stricmp โดยปกติจะเปรียบเทียบ string1 และ string2 หลังจากแปลงอักขระแต่ละตัวเป็นตัวพิมพ์เล็กแล้วส่งกลับค่าที่ระบุถึงความสัมพันธ์


2

รหัส ASCII ทศนิยมAคือ65สำหรับ_เป็น95และaเป็น97เพื่อให้strcmp()มันทำสิ่งที่มันคิดว่าจะทำอย่างไร การพูดด้วยคำพูด_เล็กaและใหญ่กว่าAนั้น

strcasecmp()จะถือว่าAเป็นa* และเนื่องจากaมีขนาดใหญ่กว่า_เอาต์พุตก็ถูกต้องเช่นกัน

* มาตรฐาน POSIX.1-2008 พูดถึงฟังก์ชั่นเหล่านี้ (strcasecmp () และ strncasecmp ()):

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

ที่มา: http://man7.org/linux/man-pages/man3/strcasecmp.3.html


3
ประเด็นของ OP คือว่าA"ใหญ่กว่า" _เมื่อเปรียบเทียบกับตัวพิมพ์ใหญ่ - เล็กและสงสัยว่าทำไมผลลัพธ์ไม่เหมือนกันเมื่อเปรียบเทียบกับตัวพิมพ์ใหญ่ - เล็ก
anton.burger

6
คำสั่งSince strcasecmp () `เป็นกรณีที่ไม่รู้สึกว่ามันจะถือว่า A เป็น a` เป็นการหักที่ไม่ถูกต้อง รูทีนแบบ case-insensitive สามารถปฏิบัติต่อตัวอักษรตัวพิมพ์ใหญ่ทั้งหมดราวกับว่าเป็นตัวอักษรตัวพิมพ์เล็กสามารถรักษาตัวอักษรตัวเล็กทั้งหมดราวกับว่าเป็นตัวอักษรตัวพิมพ์ใหญ่หรือสามารถรักษาตัวอักษรตัวพิมพ์ใหญ่แต่ละตัวได้ ถึงตัวละครที่ไม่ใช่ตัวอักษรที่มีค่าดิบของพวกเขา คำตอบนี้ไม่ได้ระบุเหตุผลในการเลือกความเป็นไปได้ใด ๆ (เหตุผลที่ถูกต้องซึ่งเป็นเอกสารที่ระบุว่าใช้ตัวพิมพ์เล็ก)
Eric Postpischil

@EricPostpischil มาตรฐาน POSIX.1-2008 พูดถึงฟังก์ชั่นเหล่านี้ (strcasecmp () และ strncasecmp ()): เมื่อหมวดหมู่ LC_CTYPE ของโลแคลที่ใช้อยู่มาจากโลแคล POSIX ฟังก์ชันเหล่านี้จะทำงานเสมือนว่าสตริงถูกแปลงเป็น ตัวพิมพ์เล็กจากนั้นทำการเปรียบเทียบแบบไบต์ มิฉะนั้นผลลัพธ์จะไม่ได้รับการระบุ
anastaciu
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.