2

这是我使用的程序。

clear
echo enter a string
read string
length=`echo -n $string | wc -c`
v=0
cons=0
d=0
s=0
len=$length
while [ $len -gt 0 ]
do
h=`echo $string | cut -c $len`
case $h in
[AaEeIiOoUu]) v=`expr $v + 1`
;;
[BbCcDdEeFfGgHhIiJjKkLlMmNnOoPpQqRrSsTtUuVvWwXxYyZz]) cons=`expr $cons + 1`
;;
[0-9]) d=`expr $d + 1`
;;
' ') s=`expr $s + 1 `
;;
esac
len=`expr $len - 1 `
done
spl=`expr $len - $v - $cons - $d - $s`
echo "vowels = $v"
echo "consonants = $cons"
echo "digits = $d"
echo "spaces = $s"
echo "special character = $spl"

该程序计算除特殊字符之外的所有其他类型的字符。即使输入值中有特殊字符,输出也会显示负值。如何修改程序以使其在输入中显示正确数量的特殊字符?

4

2 回答 2

6

虽然您的脚本在技术上(几乎)是正确的,但它并不是很漂亮(无论是在视觉上还是在代码美感方面)。

您的版本没有按预期工作的原因@devnull 已经指出,但还有另一个错误,我将在下面进一步解释。

由于您正在使用bash,您可以以更惯用、更易读和更短的方式重写整个内容。

这是您的脚本的重写版本(评论和解释如下):

#!/bin/bash

clear
IFS= read -p "enter a string: " string

for ((i = 0; i < ${#string}; i++)); do
        case "${string:$i:1}" in
                [AaEeIiOoUu]) ((vowels++)) ;;
                [[:alpha:]])  ((consonants++)) ;;
                [[:digit:]])  ((digits++)) ;;
                [[:space:]])  ((whitespace++)) ;;
                *)            ((others++)) ;;
        esac
done

echo "vowels           = ${vowels-0}"
echo "consonants       = ${consonants-0}"
echo "digits           = ${digits-0}"
echo "whitespace       = ${whitespace-0}"
echo "other characters = ${others-0}"

缩进

首先,您应该始终缩进您的代码块(if构造、循环、switch ( case) 语句,...)以提高可读性,例如

while [ $len -gt 0 ]; do
    do_stuff
done

读取、空格和提示

由于您正在使用bash,read能够为您显示提示 -echo不需要额外的。此外,除非您设置为空字符串,read否则会去除导致计算错误的前导和尾随空格:IFS

IFS= read -p "this is my prompt: " string

遍历字符串中的字符

您可以使用参数扩展来获取字符串的长度以及使用 for 循环一次切出一个字符,摆脱不必要的cut并避免子外壳。

# ${#string} = length of $string
for ((i = 0; i < ${#string}; i++)); do
    c=${string:$i:1} # c is set to the character at position $i in string $string
done

字符类

首先,您的辅音声明仍然包括Ii匹配。从技术上讲,这并不重要,因为您不能从元音匹配中掉线,但如果这是一个作业,您可能想要删除它。

话虽如此,您可以只使用短字符类来提高可读性:

[AaEeIiOoUu]) vowel_stuff     ;;
[a-zA-Z])     consonant_stuff ;; # vowels already matched above, so superfluous characters don't matter here

为了让您的生活更轻松,您可以使用所谓的字符类,例如

[:digit:] = [0-9]
[:space:] = tabs, newline, form feed, carriage return, space

等等。请注意,您当前的语言环境会影响某些字符类别。

特殊字符大小写

只需使用 switch 语句中的默认情况来计算它们,然后您可以跳过之后的计算:

case ... in
    vowels)     handle_vowel ;;
    [...]
    *)          handle_other_character ;;
esac

默认值

使用参数扩展,您还可以摆脱使用 初始化变量0,如果变量未设置,您可以即时将变量扩展为0,即它们在循环期间没有递增。

反引号

除非您编写的代码必须是超级可移植的并且必须在各种旧 shell 中工作,否则请使用$()语法而不是``.

算术表达式

和上面一样,除非你真的需要它,否则你可以(( ))用于算术表达式,例如

a=5
((a = a + 10))  # or even ((a += 10))
# $a is now 15

Google 和Advanced Bash-Scripting Guide以及Greg 的 Wikibash部分都是您的朋友。

于 2013-09-12T07:24:55.560 回答
5

您在计算时使用了错误的变量spl。使用len显然是错误的,因为您在循环中递减它并且它0在循环完成时变为。

而不是说:

spl=`expr $len - $v - $cons - $d - $s`

说:

spl=`expr $length - $v - $cons - $d - $s`
于 2013-09-12T05:20:20.567 回答