{ }codelper

学习 › Java

数据 · 11

处理单个字符

把一个字符当作值:编码、字符运算、第 i 个字符、重复。

单个字符也是值。
它内部是一个数(编码),所以可以加减。
下面的例子把 c2a3z1 这样一个小写字母、一个数字交替出现的字符串每次读两个字符,逐行输出字母、它的编码、字母表中的下一个字母,以及把这个字母按数字重复若干次得到的字符串。

import java.util.Scanner; public class Main {    public static void main(String[] args) {        String s = new Scanner(System.in).next();        for (int i = 0; i < s.length(); i += 2) {            char ch = s.charAt(i);             // i번째 글자            int count = s.charAt(i + 1) - '0'; // 숫자 글자 → 수            int code = ch;            char after = (char) ('a' + (ch - 'a' + 1) % 26); // 다음 글자(z 다음은 a)            System.out.println(ch + " " + code + " " + after + " " + String.valueOf(ch).repeat(count));        }    }}
stdin
c2a3z1
stdout
c 99 d cc
a 97 b aaa
z 122 a z

要点

  • C、C++、Java、C# 有只存一个字符的类型 char,用单引号书写('a')。
    Python、JavaScript、Craykon 没有这种类型,一个字符也只是长度为 1 的字符串。
  • 字符运算:字母的编码是连续的(a 97,b 98,…)。
    所以 ch - 'a' 表示它在 a 之后第几位(0~25),'a' + k 就是 a 之后第 k 个字母。
    没有 char 的语言要先转成编码计算,再转回字符。
  • 数字字符不是数。
    '7' 的编码是 55。
    想得到数 7,要减去 '0' 的编码('7' - '0'),或者使用转成数的函数。
  • 第 i 个字符用 s[i] 取出(Java 用 s.charAt(i))。
    最后一个字符的下标是 长度 - 1。
    再往后读,有的语言报错停止,有的悄悄给出 undefined,而 C、C++ 根本不检查,会发生什么没有规定。
  • 重复是把同一个字符串连接 n 次。
    "ab" 重复 3 次是 "ababab",0 次是空字符串。
    输出星号或横线时可以不用循环。

各语言的写法

Python编码 ord(ch),转回字符 chr(n) — 下一个字母是 chr(ord(ch) + 1)。
数字字符用 int(ch)。
s[i],从末尾数用 s[-1]。
越界是 IndexError。
重复用 ch * 3
JavaScript编码 ch.charCodeAt(0),转回字符 String.fromCharCode(n)。
数字字符用 Number(ch)。
s[i],从末尾数用 s.at(-1)。
越界的 s[9] 不报错,得到 undefined。
重复用 ch.repeat(3)
TypeScript和 JavaScript 一样
Javachar ch = s.charAt(i) — 不能写 s[i]。
编码是 int code = ch,转回字符是 (char) (ch + 1)。
数字字符用 ch - '0'。
越界是 StringIndexOutOfBoundsException。
只有字符串能重复,所以写 String.valueOf(ch).repeat(3)
C#char ch = s[i],最后一个是 s[^1]。
编码是 int code = ch,转回字符是 (char)(ch + 1)。
数字字符用 ch - '0'。
越界是 IndexOutOfRangeException。
重复字符用 new string(ch, 3)
C++char ch = s[i] — char 本身就是小整数,可以直接写 ch + 1、ch - '0'。
s[i] 不检查范围,读取大于长度的下标是未定义行为(可能读到乱七八糟的值,也可能崩溃);s.at(i) 会检查并抛出 std::out_of_range。
重复字符用 std::string(3, ch)(个数在前)
Cchar ch = s[i] — 和 C++ 一样,char 是小整数。
printf 中 %c 按字符输出,%d 按编码输出。
s[长度] 处是结束标记 \0,再往后读不做检查,属于未定义行为(可能读到乱七八糟的值,也可能崩溃)。
没有重复功能,用循环输出
Craykon编码 字符.编码(ch),转回字符 字符.从编码(n)。
数字字符用 ch.转数字()。
s[i] — 越界会报“字符串只有 0~2 号”的错误(负数下标也一样)。
重复用 ch.重复(3)

常见错误

给字符加 1 后输出,结果是个数Java · C# · C++

System.out.println('a' + 1);   // Java
Console.WriteLine('a' + 1);    // C#
std::cout << 'a' + 1;          // C++

→ 三个都输出 98 — char 加上一个数,结果是 int

转回字符:(char) ('a' + 1) → b。
Java、C# 中 char c = 'a'; c = c + 1; 因为同样的原因是编译错误,要写 c++ 或 c = (char) (c + 1)。

把数字字符直接当成数用C · C++ · Java · C#

char ch = '7';
int n = ch;
printf("%d\n", n);

→ 55 — 不是 7,而是 '7' 的编码

减去 '0':ch - '0' → 7。
因为数字字符的编码从 '0'(48)开始是连续的。

给单字符的字符串加 1Python · JavaScript · TypeScript · Craykon

print("a" + 1)

→ Python 报 TypeError: can only concatenate str (not "int") to str。JavaScript、TypeScript、Craykon 不报错,得到 a1 — 把数接在后面

先转成编码再加,然后转回字符:Python chr(ord("a") + 1),JavaScript String.fromCharCode("a".charCodeAt(0) + 1),Craykon 字符.从编码(字符.编码("a") + 1)。

各语言的差异
重复很容易和乘法混淆。
Python 中 "3" * 2 不是 6 而是 "33",所以没把输入转成数就去乘,会变成字符串重复。
JavaScript 正相反,"3" * 2 是 6;TypeScript 是编译错误;Craykon 会提示“* 运算只能用于两个数”并停止。
C++ 的 std::string(3, '*') 个数在前。
如果顺序写反成 std::string('*', 3),不会报错,却得到一个莫名其妙的 42 个字符的字符串('*' 的编码是 42)。

练习题