【问题标题】:Trying to convert uppercase char to lowercase in C without using a function尝试在不使用函数的情况下将 C 中的大写字符转换为小写
【发布时间】:2015-05-08 15:30:23
【问题描述】:

我试图在不使用函数的情况下将 char *argv[] 数组中的 char 元素从大写转换为小写。我想在 ascii 整数上加 32。

当我尝试将变量作为参数传递时,它会显示整数和,但不会显示新的小写字符。相反,它显示以下输出:

letter h, 104
tolower: 136, �

代码:

int i = 0;
for(i = 0; argv[1][i] != '\0'; i++) {
    char letter = argv[1][i];

    printf("letter %c, %i\n", letter, letter);
    char tolower = argv[1][i];
    int lowercase = tolower + 32;

    printf("tolower: %i, %c\n", lowercase, lowercase);
}

为什么要打印“?”字符?

【问题讨论】:

  • 不要调用变量tolower它是标准函数的名称。
  • h 已经是小写了。
  • 你必须检查letter >= 'A' && letter <= 'Z'

标签: c arrays uppercase lowercase


【解决方案1】:

首先,不要假设您使用的是 ascii 字符集(即,在某些字符集中,32 是错误的附加值)。您遇到的问题是 'h' + 32 不是小写的“h”。 'h' 已经是小写了,所以你想加 0。检查一下;类似:

if( tolower >= 'A' && tolower <= 'Z' )
  tolower += 'a' - 'A';

【讨论】:

  • 糟糕。 &amp;&amp; 是有意的。
  • @MattMcNabb if &amp; 是“正确的”但&amp;&amp;“惯用的”为什么MSCV 发出警告C4554: '&' : 检查运算符优先级是否有可能的错误;用括号来说明优先级?这是不正确的,这是错误的。
  • @WeatherVane 不要让我解释为什么 MSVC 会做任何事情
  • @MattMcNabb 我要求您解释您的评论。为什么按位与布尔值混合是正确的?
  • @WeatherVane &amp; 的代码符合 C 标准并正确实现了程序员的意图。但是,使用 &amp;&amp; 代替是更常见的做法和更好的风格。
【解决方案2】:

我不会指出其他回答者所做的问题,我将展示一个巧妙的技巧来执行上下交换。对于字母来说,小写字母和大写字母的区别是ascii码中的第5位。所以要设置字母小写你需要设置这个位:

lower = 0x20 | letter;

对于大写重置位:

upper = (~0x20) & letter;

如果要交换大小写,您可以使用 XOR:

swapped = 0x20 ^ letter;

这里的好处是您不必担心并检查这封信是否已经是您需要的情况。

当然这里的假设是您的系统使用的是 ASCII 编码。

【讨论】:

    【解决方案3】:

    Ascii 136 不是可打印字符。您应该执行lowercase = tolower - 'A' + 'a'; 之类的操作,但前提是tolower 肯定是大写的(不在您的示例中)。

    【讨论】:

    • ASCII 136 根本不是一个字符。 ASCII 有 128 个代码点,编号为 0-127,编码为一个字节,从 0x00 到 0x7F。但是,无论如何,@JAS 可能不使用 ASCII。
    【解决方案4】:

    这是来自我自己的 C++ 库,但它也适用于 C。

    自优化(库函数)

    // return the specified letter in lowercase
    char tolower(int c)
    {
        // (int)a = 97, (int)A = 65
        // (a)97 - (A)65 = 32
        // therefore 32 + 65 = a
        return c > 64 && c < 91 ? c + 32 : c;
    }
    // return the specfied letter in uppercase
    char toupper(int c)
    {
        // (int)a = 97, (int)A = 65
        // (a)97 - (A)65 = 32
        // therefore 97 - 32 = A
        return c > 96 && c < 123 ? c - 32 : c;
    }
    

    返回值是一个可打印字符。然而,一些现代编译器 如果您要按照以下方式进行操作,可能已经优化了代码。

    可读

    char tolower(int c)
    {
        return c >= 'A' && c <= 'Z' ? c + ('a' - 'A') : c;
    }
    char toupper(int c)
    {
        return c >= 'a' && c <= 'z' ? c - ('a' - 'A') : c;
    }
    

    请注意,aA 之间的差异是一个 32 常数。

    【讨论】:

    • 不要对 ASCII 值进行硬编码:如果要使用这种方法,至少要使其更易读,如 return (c &gt;= 'A' &amp;&amp; c &lt;= 'Z') ? c + 'a' - 'A' : c; 等。
    • @chqrlie 好吧,你说得有道理,因为这应该是一个可读的答案
    • 你为什么坚持32而不是'a' - 'A'
    • 恐怕你的推理不正确。除非您在禁用优化的情况下进行编译,否则'a' - 'A' 不会生成实际的减法,它是一个将在编译时评估的常量表达式,因此根本不会降低性能。建议使用'a' - 'A' 而不是硬编码32 的原因是可移植性和可读性。 32 是一个适用于 ASCII 的常量,但不适用于其他一些编码,并且在读者必须了解此算法不需要的有关 ASCII 的详细信息的意义上说是神奇的。
    • 代码return (c &gt;= 'A' &amp;&amp; c &lt;= 'Z') ? c + 'a' - 'A' : c;确实对字符的编码做了一些假设:它们按字母顺序编码,形成一个连续的序列,因此大写字母与其对应的小写字母之间的偏移量是恒定的.前两个假设暗示第三个假设并且对于 ASCII 是正确的,但是对于 EBCDIC,第二个假设是错误的,尽管第三个仍然成立。对 EBCDIC 使用此函数将正确地小写所有大写字母并保持小写字母不变,但也会更改一些其他字符值。
    猜你喜欢
    • 1970-01-01
    • 2020-02-25
    • 2022-01-19
    • 2023-01-12
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-04-04
    • 1970-01-01
    相关资源
    最近更新 更多