字符串图解与可视化

字符串(String)

介绍

字符串是计算机科学中最常用的数据类型之一,它是由一系列字符组成的有限序列。在大多数编程语言中,字符串被作为基本数据类型或对象提供,用于表示文本。

从本质上讲,字符串可以看作是一个数组,但与普通数组不同,字符串有特殊的属性和操作方法,更适合处理文本数据。在Java等现代编程语言中,字符串是不可变的对象,这意味着一旦创建,其内容不能被修改。

核心特性

不可变性:在Java中,字符串对象一旦创建,其值不能被更改

字符序列:由多个字符按顺序排列组成

索引访问:可以通过索引访问单个字符,索引从0开始

字符串池:Java中常量字符串会被存储在字符串池中以节省内存

Unicode支持:可以包含任何Unicode字符,支持多语言文本

基本操作

代码实现

Java

▼Java复制代码// 创建字符串

String greeting = "你好,世界!";

String name = new String("Java编程");

// 字符串长度

int length = greeting.length(); // 6

// 连接字符串

String message = greeting + " 欢迎学习" + name;

String sameMassage = greeting.concat(" 欢迎学习").concat(name);

// 访问字符

char firstChar = greeting.charAt(0); // '你'

// 获取子字符串

String subStr = greeting.substring(0, 2); // "你好"

// 字符串比较

boolean isEqual = greeting.equals("你好,世界!"); // true

boolean ignoreCase = "Java".equalsIgnoreCase("java"); // true

// 查找

int index = message.indexOf("欢迎"); // 返回"欢迎"在字符串中首次出现的索引

boolean contains = message.contains("Java"); // true

// 替换

String newStr = greeting.replace('你', '我'); // "我好,世界!"

// 分割

String[] parts = "苹果,香蕉,橙子".split(","); // ["苹果", "香蕉", "橙子"]

// 转换大小写(仅适用于拉丁字母)

String upper = "hello".toUpperCase(); // "HELLO"

String lower = "HELLO".toLowerCase(); // "hello"

// 去除首尾空白

String trimmed = " hello ".trim(); // "hello"

JavaScript

▼Javascript复制代码// 创建字符串

let greeting = "你好,世界!";

let name = "JavaScript编程";

// 字符串长度

let length = greeting.length; // 6

// 连接字符串

let message = greeting + " 欢迎学习" + name;

let sameMassage = greeting.concat(" 欢迎学习", name);

// 访问字符

let firstChar = greeting[0]; // '你'

// 或者使用charAt

let firstCharAlt = greeting.charAt(0); // '你'

// 获取子字符串

let subStr = greeting.substring(0, 2); // "你好"

// 或者使用slice

let subStrAlt = greeting.slice(0, 2); // "你好"

// 字符串比较

let isEqual = greeting === "你好,世界!"; // true

let ignoreCase = "JavaScript".toLowerCase() === "javascript"; // true

// 查找

let index = message.indexOf("欢迎"); // 返回"欢迎"在字符串中首次出现的索引

let contains = message.includes("JavaScript"); // true

// 替换

let newStr = greeting.replace('你', '我'); // "我好,世界!"

// 全局替换

let globalReplace = "hello hello".replace(/hello/g, "hi"); // "hi hi"

// 分割

let parts = "苹果,香蕉,橙子".split(","); // ["苹果", "香蕉", "橙子"]

// 转换大小写

let upper = "hello".toUpperCase(); // "HELLO"

let lower = "HELLO".toLowerCase(); // "hello"

// 去除首尾空白

let trimmed = " hello ".trim(); // "hello"

// 模板字符串(ES6特性)

let template = `${greeting},欢迎学习${name}!`;

Python

▼Python复制代码# 创建字符串

greeting = "你好,世界!"

name = "Python编程"

# 字符串长度

length = len(greeting) # 6

# 连接字符串

message = greeting + " 欢迎学习" + name

# 或者使用join

message_alt = "".join([greeting, " 欢迎学习", name])

# 或者使用格式化

message_format = f"{greeting} 欢迎学习{name}"

# 访问字符

first_char = greeting[0] # '你'

# 获取子字符串

sub_str = greeting[0:2] # "你好"

# 字符串比较

is_equal = greeting == "你好,世界!" # True

ignore_case = "Python".lower() == "python" # True

# 查找

index = message.find("欢迎") # 返回"欢迎"在字符串中首次出现的索引

contains = "Python" in message # True

# 替换

new_str = greeting.replace('你', '我') # "我好,世界!"

# 限制替换次数

limited_replace = "hello hello".replace("hello", "hi", 1) # "hi hello"

# 分割

parts = "苹果,香蕉,橙子".split(",") # ["苹果", "香蕉", "橙子"]

# 转换大小写

upper = "hello".upper() # "HELLO"

lower = "HELLO".lower() # "hello"

# 去除首尾空白

trimmed = " hello ".strip() # "hello"

# 去除左侧空白

left_trimmed = " hello ".lstrip() # "hello "

# 去除右侧空白

right_trimmed = " hello ".rstrip() # " hello"

# 检查前缀后缀

starts_with = greeting.startswith("你好") # True

ends_with = greeting.endswith("!") # True

# 字符串格式化

formatted = "{},欢迎学习{}!".format(greeting, name)

Go

▼Go复制代码package main

import (

"fmt"

"strings"

"unicode/utf8"

)

func main() {

// 创建字符串

greeting := "你好,世界!"

name := "Go编程"

// 字符串长度(注意:对于多字节字符,len返回的是字节数,不是字符数)

byteLength := len(greeting) // 返回字节数

// 获取字符数(Unicode字符)

charLength := utf8.RuneCountInString(greeting) // 6

// 连接字符串

message := greeting + " 欢迎学习" + name

// 使用fmt.Sprintf连接

messageFmt := fmt.Sprintf("%s 欢迎学习%s", greeting, name)

// 使用strings.Join连接

messageJoin := strings.Join([]string{greeting, " 欢迎学习", name}, "")

// 访问字符(Go中字符串是只读字节序列,需要先转换为rune切片才能正确处理Unicode字符)

runes := []rune(greeting)

firstChar := string(runes[0]) // "你"

// 获取子字符串

// 对于ASCII字符可以直接切片,对于Unicode字符需要转换为rune切片

runeSlice := []rune(greeting)

subStr := string(runeSlice[0:2]) // "你好"

// 字符串比较

isEqual := greeting == "你好,世界!" // true

ignoreCase := strings.EqualFold("Go", "go") // true(不区分大小写比较)

// 查找

index := strings.Index(message, "欢迎") // 返回"欢迎"在字符串中首次出现的索引

contains := strings.Contains(message, "Go") // true

// 替换

newStr := strings.Replace(greeting, "你", "我", 1) // "我好,世界!"

// 替换所有

allReplaced := strings.ReplaceAll("hello hello", "hello", "hi") // "hi hi"

// 分割

parts := strings.Split("苹果,香蕉,橙子", ",") // ["苹果", "香蕉", "橙子"]

// 转换大小写

upper := strings.ToUpper("hello") // "HELLO"

lower := strings.ToLower("HELLO") // "hello"

// 去除首尾空白

trimmed := strings.TrimSpace(" hello ") // "hello"

// 前缀和后缀

hasPrefix := strings.HasPrefix(greeting, "你好") // true

hasSuffix := strings.HasSuffix(greeting, "!") // true

// 输出结果示例

fmt.Println(message, charLength, firstChar, subStr, isEqual, index, newStr, parts, trimmed)

}

C

▼C复制代码#include

#include

#include

#include

#include

int main() {

// 创建字符串(C中字符串是字符数组,以'�'结尾)

char greeting[] = "Hello, world!"; // ASCII字符

// 注意:C的原生char类型不支持Unicode,需要使用特定库如wchar_t或UTF-8编码

// 字符串长度

int length = strlen(greeting); // 13

// 连接字符串

char message[100]; // 预先分配足够空间

strcpy(message, greeting);

strcat(message, " Welcome to ");

strcat(message, "C programming");

// 另一种连接方式

char buffer[100];

sprintf(buffer, "%s Welcome to %s", greeting, "C programming");

// 访问字符

char firstChar = greeting[0]; // 'H'

// 获取子字符串(C中需要手动实现)

char subStr[10];

strncpy(subStr, greeting, 5);

subStr[5] = '�'; // 需要手动添加结束符

// 字符串比较

int isEqual = strcmp(greeting, "Hello, world!") == 0; // 1 (true)

// 不区分大小写比较(需要使用特定函数)

#ifdef _MSC_VER

int ignoreCase = _stricmp("C", "c") == 0; // Windows

#else

int ignoreCase = strcasecmp("C", "c") == 0; // POSIX

#endif

// 查找

char *found = strstr(message, "Welcome"); // 返回指向"Welcome"的指针或NULL

int index = found ? found - message : -1; // 计算索引位置

bool contains = found != NULL; // true

// 替换(C中需要手动实现)

char replaced[100];

strcpy(replaced, greeting);

// 查找并替换第一个'l'为'L'

char *pos = strchr(replaced, 'l');

if (pos != NULL) {

*pos = 'L';

}

// 分割字符串

char fruits[] = "apple,banana,orange";

char *token = strtok(fruits, ",");

while (token != NULL) {

printf("%s

", token);

token = strtok(NULL, ",");

}

// 转换大小写

char upper[100];

strcpy(upper, "hello");

for (int i = 0; upper[i]; i++) {

upper[i] = toupper(upper[i]);

}

char lower[100];

strcpy(lower, "HELLO");

for (int i = 0; lower[i]; i++) {

lower[i] = tolower(lower[i]);

}

// 去除首尾空白(C中需要手动实现)

char trimmed[] = " hello ";

char *trim_start = trimmed;

char *trim_end = trimmed + strlen(trimmed) - 1;

// 去除前导空格

while (*trim_start && isspace(*trim_start)) {

trim_start++;

}

// 去除尾部空格

while (trim_end > trim_start && isspace(*trim_end)) {

*trim_end-- = '�';

}

printf("Original: '%s', Trimmed: '%s'

", trimmed, trim_start);

return 0;

}

C++

▼C++复制代码#include

#include

#include

#include

#include

int main() {

// 创建字符串

std::string greeting = "你好,世界!";

std::string name = "C++编程";

// 字符串长度

size_t length = greeting.length(); // 返回Unicode码点数量,对于中文字符需要注意编码

// 连接字符串

std::string message = greeting + " 欢迎学习" + name;

// 使用append

std::string sameMessage = greeting;

sameMessage.append(" 欢迎学习").append(name);

// 使用ostringstream连接

std::ostringstream oss;

oss << greeting << " 欢迎学习" << name;

std::string ossMessage = oss.str();

// 访问字符

char firstChar = greeting[0]; // 注意:对于多字节字符可能不正确

// 获取子字符串

std::string subStr = greeting.substr(0, 2); // 注意:对于UTF-8编码的中文,偏移量是字节而非字符

// 字符串比较

bool isEqual = (greeting == "你好,世界!"); // true

// 不区分大小写比较(C++中需要手动实现或使用库)

auto compareIgnoreCase = [](const std::string& a, const std::string& b) {

if (a.size() != b.size()) return false;

return std::equal(a.begin(), a.end(), b.begin(),

[](char a, char b) {

return std::tolower(a) == std::tolower(b);

});

};

bool ignoreCase = compareIgnoreCase("C++", "c++"); // true

// 查找

size_t index = message.find("欢迎"); // 返回"欢迎"在字符串中首次出现的索引

bool contains = (message.find("C++") != std::string::npos); // true

// 替换

std::string newStr = greeting;

size_t pos = newStr.find("你");

if (pos != std::string::npos) {

newStr.replace(pos, 3, "我"); // "我好,世界!"(UTF-8中"你"占3字节)

}

// 全部替换

std::string text = "hello hello";

std::string pattern = "hello";

std::string replacement = "hi";

size_t start_pos = 0;

while ((start_pos = text.find(pattern, start_pos)) != std::string::npos) {

text.replace(start_pos, pattern.length(), replacement);

start_pos += replacement.length();

}

// 分割字符串

std::string fruitStr = "苹果,香蕉,橙子";

std::vector fruits;

std::string token;

std::istringstream tokenStream(fruitStr);

while (std::getline(tokenStream, token, ',')) {

fruits.push_back(token);

}

// 转换大小写

std::string upper = "hello";

std::transform(upper.begin(), upper.end(), upper.begin(),

[](unsigned char c){ return std::toupper(c); }); // "HELLO"

std::string lower = "HELLO";

std::transform(lower.begin(), lower.end(), lower.begin(),

[](unsigned char c){ return std::tolower(c); }); // "hello"

// 去除首尾空白

auto trim = [](std::string& s) {

s.erase(s.begin(), std::find_if(s.begin(), s.end(), [](unsigned char ch) {

return !std::isspace(ch);

}));

s.erase(std::find_if(s.rbegin(), s.rend(), [](unsigned char ch) {

return !std::isspace(ch);

}).base(), s.end());

return s;

};

std::string trimmed = " hello ";

trim(trimmed); // "hello"

// 输出示例

std::cout << "Message: " << message << std::endl;

std::cout << "First char: " << firstChar << std::endl;

std::cout << "Contains C++: " << std::boolalpha << contains << std::endl;

return 0;

}

优点

易用性:提供了丰富的API和操作方法,处理文本更简单

国际化支持:支持Unicode字符集,可以处理各种语言的文本

内存优化:字符串池机制减少内存使用

应用场景

文本处理:处理用户输入、配置文件、日志等

数据解析:解析JSON、XML、CSV等格式的数据

自然语言处理:文本分析、情感分析、机器翻译等

网络通信:HTTP请求参数、URL处理、网络协议等

用户界面:显示文本、多语言支持等

扩展

StringBuilder vs StringBuffer vs String

String:不可变,适合作为常量使用

StringBuilder:可变,非线程安全,适合在单线程环境中频繁修改字符串

StringBuffer:可变,线程安全,适合在多线程环境中使用,但性能略低于StringBuilder

字符串编码

字符串在内存中存储和在文件中保存时需要特定的编码方式,常见的编码包括ASCII、UTF-8、UTF-16等。

▼Java复制代码// 字符串与字节数组转换

String str = "你好,世界";

// 将字符串转换为UTF-8编码的字节数组

byte[] bytes = str.getBytes("UTF-8");

// 将字节数组转回字符串

String newStr = new String(bytes, "UTF-8");

// 检查编码是否支持

boolean supported = Charset.isSupported("UTF-8"); // true

常见编码比较

ASCII:只能表示128个字符,主要是英文字母、数字和符号

ISO-8859-1:扩展ASCII,可表示西欧语言字符

UTF-8:变长编码,英文占1字节,中文通常占3字节,兼容ASCII

UTF-16:Java内部使用的编码,所有字符占2或4字节

GBK/GB2312:中文编码标准,主要用于表示中文字符

测验

在Java中,String s1 = "hello"; 和 String s2 = new String("hello"); 创建的字符串有什么区别?

测验答案

s1使用字符串字面量创建,会首先在字符串常量池中查找"hello",如果存在则直接引用,不存在则在池中创建并引用。

s2使用构造函数创建,会在堆内存中创建一个新的String对象,即使常量池中已有相同内容的字符串。

所以s1 == s2返回false(比较引用),但s1.equals(s2)返回true(比较内容)。

相关LeetCode热门题目

字符串是编程中最常见的数据类型之一,是必须要掌握的内容。这里给大家推荐一些相关题目来练手:

14. 最长公共前缀 - 力扣(LeetCode) - 查找字符串数组的最长公共前缀

20. 有效的括号 - 判断字符串中的括号是否有效匹配

415. 字符串相加 - 将两个数字字符串相加