Là thuật toán strcasecmp thiếu sót?


34

Tôi đang cố gắng thực hiện lại strcasecmpchức năng trong C và tôi nhận thấy những gì dường như không nhất quán trong quá trình so sánh.

Từ man strcmp

Hàm strcmp () so sánh hai chuỗi s1 và s2. Bản địa không được tính đến (để so sánh nhận biết bản địa, xem strcoll (3)). Nó trả về một số nguyên nhỏ hơn, bằng hoặc lớn hơn 0 nếu tìm thấy s1 tương ứng nhỏ hơn, để khớp hoặc lớn hơn s2.

Từ man strcasecmp

Hàm strcasecmp () thực hiện so sánh từng byte của chuỗi s1 và s2, bỏ qua trường hợp của các ký tự. Nó trả về một số nguyên nhỏ hơn, bằng hoặc lớn hơn 0 nếu tìm thấy s1 tương ứng nhỏ hơn, để khớp hoặc lớn hơn s2.

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

Cho, thông tin này, tôi không hiểu kết quả của đoạn mã sau:

#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

Dường như, nếu ký tự hiện tại trong s1một chữ cái, nó luôn được chuyển đổi thành chữ thường, bất kể ký tự hiện tại trong đó s2có phải là một chữ cái hay không.

Ai đó có thể giải thích hành vi này? Không phải dòng đầu tiên và thứ ba là giống hệt nhau?

Cảm ơn bạn trước!

PS:
Tôi đang sử dụng gcc 9.2.0trên Manjaro.
Ngoài ra, khi tôi biên dịch với -fno-builtincờ tôi nhận được thay thế:

-30
2
2
2

Tôi đoán đó là vì chương trình không sử dụng các chức năng tối ưu hóa của gcc, nhưng câu hỏi vẫn còn.


2
Thêm một trường hợp thử nghiệm khác vào bộ của bạn: printf("%i\n", strcasecmp("a", "_"));Điều này có lẽ sẽ có kết quả tương tự như printf("%i\n", strcasecmp("A", "_"));Nhưng điều đó có nghĩa là một trong hai cuộc gọi không phân biệt chữ hoa chữ thường này sẽ không đồng ý với đối tác phân biệt chữ hoa chữ thường.
anton.burger

Có vẻ như mô tả về việc strcasecmpbạn đang giới thiệu là không chính xác. Thêm chi tiết trong các câu trả lời nâng cao.
Jabberwocky

9
Đó là điều duy nhất có ý nghĩa. Một chức năng cho biết A < _ && a > _ && A == asẽ gây ra rất nhiều vấn đề.
ikegami

Ngoài ra: "Tôi đang cố gắng thực hiện lại chức năng strcasecmp trong C" -> Mặc dù mã không được hiển thị, hãy chắc chắn so sánh "như thể" unsigned char. C17 / 18 "Xử lý chuỗi <string.h>" -> "Đối với tất cả các chức năng trong phần này, mỗi ký tự sẽ được hiểu như thể nó có kiểu unsigned char". Điều này tạo ra sự khác biệt khi charcác giá trị nằm ngoài phạm vi ASCII 0-127.
chux - Tái lập Monica

1
Về sự khác biệt trong kết quả đầu ra có tích hợp và không có: Cả hai đều nói giống nhau, vì kết quả của chúng giống hệt nhau <0 và> 0 và bạn không có ví dụ cho == 0. Nhưng bạn có thể thấy các thuật toán tỏa sáng thông qua: một số giá trị được trả về là sự khác biệt của ký tự không bằng nhau đầu tiên.
bận rộn

Câu trả lời:


31

Hành vi là chính xác.

Mỗi str\[n\]casecmp() thông số kỹ thuật POSIX :

Khi LC_CTYPEdanh mục của miền được sử dụng là từ miền địa phương POSIX, các hàm này sẽ hoạt động như thể các chuỗi đã được chuyển đổi thành chữ thường và sau đó thực hiện so sánh byte. Nếu không, kết quả là không xác định.

Đó cũng là một phần của phần LƯU Ý của trang người dùng Linux :

Tiêu chuẩn POSIX.1-2008 nói về các chức năng này:

Khi danh mục LC_CTYPE của miền địa phương đang được sử dụng là từ miền địa phương POSIX, các hàm này sẽ hoạt động như thể các chuỗi đã được chuyển đổi thành chữ thường và sau đó thực hiện so sánh byte. Nếu không, kết quả là không xác định.

Tại sao?

Như @HansOlsson đã chỉ ra trong câu trả lời của mình , thực hiện so sánh không phân biệt chữ hoa chữ thường giữa các chữ cái và cho phép tất cả các phép so sánh khác có kết quả "tự nhiên" như được thực hiện strcmp()sẽ phá vỡ sắp xếp.

Nếu 'A' == 'a'(định nghĩa so sánh không phân biệt chữ hoa chữ thường) '_' > 'A''_' < 'a'(kết quả "tự nhiên" trong bộ ký tự ASCII) không thể đúng cả hai.


Thực hiện so sánh không phân biệt chữ hoa chữ thường giữa các chữ cái sẽ không dẫn đến '_' > 'A' && '_' < 'a'; dường như không phải là ví dụ tốt nhất
Tiểu hành tinh có cánh

1
@AsteroidsWithWings Đó là những ký tự được sử dụng trong câu hỏi. Và nếu 'a' == 'A' theo định nghĩa , nếu bạn làm một so sánh giữa các giá trị "tự nhiên" của 'a', 'A''_, bạn không thể làm một so sánh case-insensitive giữa 'A''a'để có được sự bình đẳng và có được kết quả phân loại phù hợp.
Andrew Henle

Tôi không tranh luận về điều đó, nhưng ví dụ phản biện cụ thể mà bạn cung cấp dường như không liên quan.
Tiểu hành tinh có cánh

@AsteroidsWithWings Trải qua bài tập tinh thần xây dựng cây nhị phân từ 'a', 'A''_', trải qua tất cả 6 lệnh chèn vào cây và so sánh kết quả từ "chữ thường luôn" được chỉ định với câu hỏi "chỉ chuyển đổi câu hỏi" khi đó là so sánh giữa các chữ cái ". Ví dụ: sử dụng thuật toán sau và bắt đầu bằng '_', 'a''A'cuộn lên các mặt đối diện của cây nhưng chúng được xác định là bằng nhau. Thuật toán "chỉ chuyển đổi chữ cái thành chữ thường trong so sánh chữ cái" bị hỏng và 3 ký tự đó cho thấy điều đó.
Andrew Henle

Được rồi, sau đó tôi đề nghị chứng minh rằng trong câu trả lời bởi vì tại thời điểm này, nó chỉ nhảy lên để chỉ ra rằng " '_' > 'A' '_' < 'a'cả hai không thể là sự thật" mà không cho chúng tôi biết lý do tại sao chúng ta nên nghĩ nó sẽ xảy ra. (Đó là một nhiệm vụ cho người trả lời, không phải cho một trong số hàng triệu độc giả.)
Asteroids With Wings

21

Các liên kết khác, http://man7.org/linux/man-pages/man3/strcasecmp.3p.html cho strcasecmp nói rằng chuyển đổi sang chữ thường là hành vi đúng (ít nhất là trong ngôn ngữ POSIX).

Lý do cho hành vi đó là nếu bạn sử dụng strcasecmp để sắp xếp một chuỗi các chuỗi thì cần có kết quả hợp lý.

Mặt khác, nếu bạn cố gắng sắp xếp "A", "C", "_", "b" bằng cách sử dụng, ví dụ: qsort kết quả sẽ phụ thuộc vào thứ tự so sánh.


3
Mặt khác, nếu bạn cố gắng sắp xếp "A", "C", "_", "b" bằng cách sử dụng, ví dụ: qsort kết quả sẽ phụ thuộc vào thứ tự so sánh. Điểm tốt. Đó có thể là lý do POSIX chỉ định hành vi.
Andrew Henle

6
Cụ thể hơn, bạn cần một thứ tự tổng thể để sắp xếp, điều này sẽ không xảy ra nếu bạn xác định so sánh như trong câu hỏi (vì nó sẽ không mang tính bắc cầu).
Công tước

8

Dường như, nếu ký tự hiện tại trong s1 là một chữ cái, nó luôn được chuyển đổi thành chữ thường, bất kể ký tự hiện tại trong s2 có phải là một chữ cái hay không.

Điều đó đúng - và đó là strcasecmp()chức năng nên làm! Đây là một POSIXchức năng, chứ không phải là một phần của CTiêu chuẩn, nhưng, từ " Thông số kỹ thuật cơ sở nhóm mở, Vấn đề 6 ":

Trong ngôn ngữ POSIX, strcasecmp () và strncasecmp () sẽ hoạt động như thể các chuỗi đã được chuyển đổi thành chữ thường và sau đó thực hiện so sánh byte. Kết quả là không xác định ở các địa phương khác.

Ngẫu nhiên, hành vi này cũng phụ thuộc vào _stricmp()chức năng (như được sử dụng trong Visual Studio / MSCV):

Hàm _stricmp thường so sánh chuỗi1 và chuỗi2 sau khi chuyển đổi từng ký tự thành chữ thường và trả về một giá trị biểu thị mối quan hệ của chúng.


2

Mã số thập phân ASCII cho A65cho _95và cho a97, vì vậy strcmp()nó làm những gì nó giả sử để làm. Nói theo phương pháp học _thì nhỏ hơn avà lớn hơn A.

strcasecmp()sẽ coi Anhư là a*, và vì alớn hơn _đầu ra cũng đúng.

* Tiêu chuẩn POSIX.1-2008 nói về các chức năng này (strcasecmp () và strncasecmp ()):

Khi danh mục LC_CTYPE của miền địa phương đang được sử dụng là từ miền địa phương POSIX, các hàm này sẽ hoạt động như thể các chuỗi đã được chuyển đổi thành chữ thường và sau đó thực hiện so sánh byte. Nếu không, kết quả là không xác định.

Nguồn: http://man7.org/linux/man-pages/man3/strcasecmp.3.html


3
Quan điểm của OP Alà "lớn hơn" so với _khi so sánh trường hợp không nhạy cảm và tự hỏi tại sao kết quả không giống như khi so sánh trường hợp nhạy cảm.
anton.burger

6
Câu lệnh Since strcasecmp () `không phân biệt chữ hoa chữ thường, nó sẽ coi A là a` là một khoản khấu trừ không hợp lệ. Một thói quen không phân biệt chữ hoa chữ thường có thể coi tất cả các chữ cái viết hoa như thể chúng là chữ cái viết thường, có thể coi tất cả các chữ cái viết thường như thể chúng là chữ cái viết hoa hoặc có thể coi mỗi chữ cái viết hoa tương đương với chữ cái viết thường và ngược lại nhưng vẫn so sánh chúng để các ký tự không chữ với giá trị thô của chúng. Câu trả lời này không nêu lý do thích bất kỳ khả năng nào trong số những khả năng đó (lý do chính xác là tài liệu nói rằng sử dụng chữ thường).
Eric Postpischil

@EricPostpischil Tiêu chuẩn POSIX.1-2008 nói về các hàm này (strcasecmp () và strncasecmp ()): Khi danh mục LC_CTYPE của miền địa phương được sử dụng là từ miền địa phương POSIX, các hàm này sẽ hoạt động như thể các chuỗi đã được chuyển đổi thành chữ thường và sau đó một so sánh byte thực hiện. Nếu không, kết quả là không xác định.
anastaciu
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.