18.字符串操作

18. 字符串操作

在Java+Selenium自动化测试中,字符串操作是非常重要的技能。我们经常需要处理从网页元素中获取的文本、验证文本内容、格式化数据等。本章将详细介绍在自动化测试中常用的字符串操作方法。

目录

  1. 获取元素文本内容
  2. 字符串基本操作
  3. 字符串比较和验证
  4. 字符串格式化和转换
  5. 正则表达式在自动化测试中的应用
  6. 字符串分割和拼接
  7. 处理特殊字符和编码
  8. 实际应用场景
  9. 最佳实践和注意事项

1. 获取元素文本内容

1.1 getText() 方法

getText() 是Selenium中最常用的获取元素文本内容的方法。

基本语法

WebElement element = driver.findElement(By.id("elementId"));
String text = element.getText();

详细示例

import org.openqa.selenium.By;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.WebElement;
import org.openqa.selenium.chrome.ChromeDriver;

public class TextOperationTest {
    public static void main(String[] args) {
        WebDriver driver = new ChromeDriver();

        try {
            driver.get("https://example.com");

            // 获取标题文本
            WebElement title = driver.findElement(By.tagName("h1"));
            String titleText = title.getText();
            System.out.println("页面标题: " + titleText);

            // 获取段落文本
            WebElement paragraph = driver.findElement(By.className("content"));
            String paragraphText = paragraph.getText();
            System.out.println("段落内容: " + paragraphText);

            // 获取按钮文本
            WebElement button = driver.findElement(By.id("submitBtn"));
            String buttonText = button.getText();
            System.out.println("按钮文本: " + buttonText);

        } finally {
            driver.quit();
        }
    }
}

1.2 getAttribute() 方法获取属性值

有时候我们需要获取元素的属性值,而不是显示的文本。

// 获取input元素的value属性
WebElement inputField = driver.findElement(By.id("username"));
String inputValue = inputField.getAttribute("value");

// 获取链接的href属性
WebElement link = driver.findElement(By.tagName("a"));
String linkUrl = link.getAttribute("href");

// 获取图片的src属性
WebElement image = driver.findElement(By.tagName("img"));
String imageSrc = image.getAttribute("src");

// 获取元素的class属性
String className = element.getAttribute("class");

// 获取自定义属性
String customAttr = element.getAttribute("data-id");

1.3 getCssValue() 方法获取CSS属性值

// 获取元素的颜色
String color = element.getCssValue("color");

// 获取字体大小
String fontSize = element.getCssValue("font-size");

// 获取背景色
String backgroundColor = element.getCssValue("background-color");

System.out.println("元素颜色: " + color);
System.out.println("字体大小: " + fontSize);
System.out.println("背景色: " + backgroundColor);

2. 字符串基本操作

2.1 字符串长度和空值检查

public class StringBasicOperations {

    // 检查字符串是否为空或null
    public static boolean isStringEmpty(String str) {
        return str == null || str.trim().isEmpty();
    }

    // 安全获取字符串长度
    public static int getStringLength(String str) {
        return str == null ? 0 : str.length();
    }

    public static void main(String[] args) {
        WebDriver driver = new ChromeDriver();

        try {
            driver.get("https://example.com");

            WebElement element = driver.findElement(By.id("message"));
            String text = element.getText();

            // 检查文本是否为空
            if (isStringEmpty(text)) {
                System.out.println("元素文本为空");
            } else {
                System.out.println("文本长度: " + getStringLength(text));
                System.out.println("文本内容: " + text);
            }

        } finally {
            driver.quit();
        }
    }
}

2.2 字符串去除空格和格式化

// 去除首尾空格
String trimmedText = text.trim();

// 去除所有空格
String noSpaceText = text.replaceAll("\s+", "");

// 去除多余空格,只保留单个空格
String singleSpaceText = text.replaceAll("\s+", " ").trim();

// 转换为大写
String upperCaseText = text.toUpperCase();

// 转换为小写
String lowerCaseText = text.toLowerCase();

// 首字母大写
String capitalizedText = text.substring(0, 1).toUpperCase() + 
                        text.substring(1).toLowerCase();

System.out.println("原始文本: '" + text + "'");
System.out.println("去除空格: '" + trimmedText + "'");
System.out.println("无空格: '" + noSpaceText + "'");
System.out.println("单空格: '" + singleSpaceText + "'");
System.out.println("大写: " + upperCaseText);
System.out.println("小写: " + lowerCaseText);
System.out.println("首字母大写: " + capitalizedText);

2.3 字符串截取和替换

public class StringManipulation {
    public static void demonstrateStringOperations() {
        String originalText = "Welcome to Selenium Automation Testing";

        // 字符串截取
        String substring1 = originalText.substring(0, 7); // "Welcome"
        String substring2 = originalText.substring(11, 19); // "Selenium"
        String substring3 = originalText.substring(20); // "Automation Testing"

        // 字符串替换
        String replacedText1 = originalText.replace("Selenium", "WebDriver");
        String replacedText2 = originalText.replaceAll("\s+", "_");
        String replacedText3 = originalText.replaceFirst("e", "E");

        // 输出结果
        System.out.println("原始文本: " + originalText);
        System.out.println("截取0-7: " + substring1);
        System.out.println("截取11-19: " + substring2);
        System.out.println("从20开始: " + substring3);
        System.out.println("替换Selenium: " + replacedText1);
        System.out.println("空格替换为下划线: " + replacedText2);
        System.out.println("替换第一个e: " + replacedText3);
    }
}

3. 字符串比较和验证

3.1 基本字符串比较

public class StringComparison {

    // 精确比较(区分大小写)
    public static boolean exactMatch(String actual, String expected) {
        return actual != null && actual.equals(expected);
    }

    // 忽略大小写比较
    public static boolean caseInsensitiveMatch(String actual, String expected) {
        return actual != null && actual.equalsIgnoreCase(expected);
    }

    // 包含检查
    public static boolean contains(String actual, String expected) {
        return actual != null && actual.contains(expected);
    }

    // 开头检查
    public static boolean startsWith(String actual, String prefix) {
        return actual != null && actual.startsWith(prefix);
    }

    // 结尾检查
    public static boolean endsWith(String actual, String suffix) {
        return actual != null && actual.endsWith(suffix);
    }

    public static void main(String[] args) {
        WebDriver driver = new ChromeDriver();

        try {
            driver.get("https://example.com");

            WebElement titleElement = driver.findElement(By.tagName("h1"));
            String actualTitle = titleElement.getText();
            String expectedTitle = "Welcome to Our Website";

            // 各种比较方式
            System.out.println("精确匹配: " + exactMatch(actualTitle, expectedTitle));
            System.out.println("忽略大小写: " + caseInsensitiveMatch(actualTitle, expectedTitle));
            System.out.println("包含'Welcome': " + contains(actualTitle, "Welcome"));
            System.out.println("以'Welcome'开头: " + startsWith(actualTitle, "Welcome"));
            System.out.println("以'Website'结尾: " + endsWith(actualTitle, "Website"));

        } finally {
            driver.quit();
        }
    }
}

3.2 使用断言进行文本验证

import org.junit.Assert;
import org.testng.Assert;

public class TextAssertions {

    public void validatePageTitle(WebDriver driver, String expectedTitle) {
        String actualTitle = driver.getTitle();

        // JUnit断言
        Assert.assertEquals("页面标题不匹配", expectedTitle, actualTitle);

        // TestNG断言
        Assert.assertEquals(actualTitle, expectedTitle, "页面标题不匹配");
    }

    public void validateElementText(WebElement element, String expectedText) {
        String actualText = element.getText().trim();

        // 精确匹配
        Assert.assertEquals(actualText, expectedText, "元素文本不匹配");

        // 包含检查
        Assert.assertTrue(actualText.contains(expectedText), 
                         "元素文本不包含期望内容: " + expectedText);
    }

    public void validateTextNotEmpty(WebElement element) {
        String text = element.getText();

        Assert.assertNotNull(text, "元素文本为null");
        Assert.assertFalse(text.trim().isEmpty(), "元素文本为空");
    }
}

4. 字符串格式化和转换

4.1 数字和字符串转换

public class StringConversion {

    // 从页面元素中提取数字
    public static double extractNumber(String text) {
        // 移除非数字字符,保留小数点和负号
        String numberStr = text.replaceAll("[^\d.-]", "");
        try {
            return Double.parseDouble(numberStr);
        } catch (NumberFormatException e) {
            System.out.println("无法解析数字: " + text);
            return 0.0;
        }
    }

    // 格式化价格显示
    public static String formatPrice(double price) {
        return String.format("¥%.2f", price);
    }

    // 格式化百分比
    public static String formatPercentage(double value) {
        return String.format("%.1f%%", value * 100);
    }

    public static void main(String[] args) {
        WebDriver driver = new ChromeDriver();

        try {
            driver.get("https://shopping-site.com");

            // 获取价格文本并转换
            WebElement priceElement = driver.findElement(By.className("price"));
            String priceText = priceElement.getText(); // "¥299.99"
            double price = extractNumber(priceText);

            System.out.println("原始价格文本: " + priceText);
            System.out.println("提取的数字: " + price);
            System.out.println("格式化价格: " + formatPrice(price));

            // 计算折扣
            double discount = 0.15;
            double discountedPrice = price * (1 - discount);
            System.out.println("折扣: " + formatPercentage(discount));
            System.out.println("折后价: " + formatPrice(discountedPrice));

        } finally {
            driver.quit();
        }
    }
}

4.2 日期和时间字符串处理

import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.time.format.DateTimeParseException;

public class DateStringHandling {

    // 解析日期字符串
    public static LocalDate parseDate(String dateStr, String pattern) {
        try {
            DateTimeFormatter formatter = DateTimeFormatter.ofPattern(pattern);
            return LocalDate.parse(dateStr, formatter);
        } catch (DateTimeParseException e) {
            System.out.println("日期解析失败: " + dateStr);
            return null;
        }
    }

    // 格式化日期
    public static String formatDate(LocalDate date, String pattern) {
        DateTimeFormatter formatter = DateTimeFormatter.ofPattern(pattern);
        return date.format(formatter);
    }

    public static void main(String[] args) {
        WebDriver driver = new ChromeDriver();

        try {
            driver.get("https://booking-site.com");

            // 获取日期文本
            WebElement dateElement = driver.findElement(By.id("checkInDate"));
            String dateText = dateElement.getText(); // "2024-03-15"

            // 解析日期
            LocalDate date = parseDate(dateText, "yyyy-MM-dd");
            if (date != null) {
                // 转换为不同格式
                String chineseFormat = formatDate(date, "yyyy年MM月dd日");
                String usFormat = formatDate(date, "MM/dd/yyyy");

                System.out.println("原始日期: " + dateText);
                System.out.println("中文格式: " + chineseFormat);
                System.out.println("美式格式: " + usFormat);
            }

        } finally {
            driver.quit();
        }
    }
}

5. 正则表达式在自动化测试中的应用

5.1 常用正则表达式模式

import java.util.regex.Matcher;
import java.util.regex.Pattern;

public class RegexOperations {

    // 验证邮箱格式
    public static boolean isValidEmail(String email) {
        String emailRegex = "^[A-Za-z0-9+_.-]+@([A-Za-z0-9.-]+\.[A-Za-z]{2,})$";
        Pattern pattern = Pattern.compile(emailRegex);
        return pattern.matcher(email).matches();
    }

    // 验证手机号格式(中国)
    public static boolean isValidChinesePhone(String phone) {
        String phoneRegex = "^1[3-9]\d{9}$";
        Pattern pattern = Pattern.compile(phoneRegex);
        return pattern.matcher(phone).matches();
    }

    // 提取URL中的域名
    public static String extractDomain(String url) {
        String domainRegex = "https?://([^/]+)";
        Pattern pattern = Pattern.compile(domainRegex);
        Matcher matcher = pattern.matcher(url);
        if (matcher.find()) {
            return matcher.group(1);
        }
        return null;
    }

    // 提取文本中的所有数字
    public static String[] extractNumbers(String text) {
        String numberRegex = "\d+";
        Pattern pattern = Pattern.compile(numberRegex);
        Matcher matcher = pattern.matcher(text);

        java.util.List<String> numbers = new java.util.ArrayList<>();
        while (matcher.find()) {
            numbers.add(matcher.group());
        }

        return numbers.toArray(new String[0]);
    }

    public static void main(String[] args) {
        // 测试正则表达式
        String email = "user@example.com";
        String phone = "13812345678";
        String url = "https://www.example.com/page";
        String text = "订单号:12345,金额:299元,数量:3件";

        System.out.println("邮箱验证: " + isValidEmail(email));
        System.out.println("手机号验证: " + isValidChinesePhone(phone));
        System.out.println("提取域名: " + extractDomain(url));
        System.out.println("提取数字: " + java.util.Arrays.toString(extractNumbers(text)));
    }
}

5.2 在Selenium中使用正则表达式验证

public class RegexValidationInSelenium {

    public void validateFormFields(WebDriver driver) {
        // 验证邮箱输入框
        WebElement emailInput = driver.findElement(By.id("email"));
        String emailValue = emailInput.getAttribute("value");

        if (!isValidEmail(emailValue)) {
            System.out.println("邮箱格式不正确: " + emailValue);
        }

        // 验证手机号输入框
        WebElement phoneInput = driver.findElement(By.id("phone"));
        String phoneValue = phoneInput.getAttribute("value");

        if (!isValidChinesePhone(phoneValue)) {
            System.out.println("手机号格式不正确: " + phoneValue);
        }

        // 验证页面上的价格格式
        WebElement priceElement = driver.findElement(By.className("price"));
        String priceText = priceElement.getText();

        String priceRegex = "¥\d+\.\d{2}";
        if (!priceText.matches(priceRegex)) {
            System.out.println("价格格式不正确: " + priceText);
        }
    }

    // 使用正则表达式查找元素
    public void findElementsByRegex(WebDriver driver) {
        // 查找所有class名包含"btn"的按钮
        String xpath = "//button[contains(@class, 'btn')]";
        java.util.List<WebElement> buttons = driver.findElements(By.xpath(xpath));

        for (WebElement button : buttons) {
            String buttonText = button.getText();
            System.out.println("找到按钮: " + buttonText);
        }
    }
}

6. 字符串分割和拼接

6.1 字符串分割操作

public class StringSplitOperations {

    // 分割面包屑导航
    public static String[] splitBreadcrumb(String breadcrumb) {
        // 假设面包屑格式为: "首页 > 产品分类 > 具体产品"
        return breadcrumb.split("\s*>\s*");
    }

    // 分割标签列表
    public static String[] splitTags(String tags) {
        // 假设标签格式为: "Java, Selenium, 自动化测试, WebDriver"
        return tags.split("\s*,\s*");
    }

    // 解析表格行数据
    public static String[] parseTableRow(String rowText) {
        // 假设表格数据用制表符或多个空格分隔
        return rowText.split("\s{2,}|\t");
    }

    public static void main(String[] args) {
        WebDriver driver = new ChromeDriver();

        try {
            driver.get("https://example.com");

            // 处理面包屑导航
            WebElement breadcrumbElement = driver.findElement(By.className("breadcrumb"));
            String breadcrumbText = breadcrumbElement.getText();
            String[] breadcrumbParts = splitBreadcrumb(breadcrumbText);

            System.out.println("面包屑导航:");
            for (int i = 0; i < breadcrumbParts.length; i++) {
                System.out.println("  " + i + ": " + breadcrumbParts[i]);
            }

            // 处理标签列表
            WebElement tagsElement = driver.findElement(By.className("tags"));
            String tagsText = tagsElement.getText();
            String[] tagArray = splitTags(tagsText);

            System.out.println("标签列表:");
            for (String tag : tagArray) {
                System.out.println("  - " + tag);
            }

        } finally {
            driver.quit();
        }
    }
}

6.2 字符串拼接操作

public class StringConcatenation {

    // 构建搜索查询字符串
    public static String buildSearchQuery(String[] keywords) {
        return String.join(" AND ", keywords);
    }

    // 构建文件路径
    public static String buildFilePath(String... pathParts) {
        return String.join(System.getProperty("file.separator"), pathParts);
    }

    // 构建URL参数
    public static String buildUrlParams(java.util.Map<String, String> params) {
        java.util.List<String> paramList = new java.util.ArrayList<>();
        for (java.util.Map.Entry<String, String> entry : params.entrySet()) {
            paramList.add(entry.getKey() + "=" + entry.getValue());
        }
        return String.join("&", paramList);
    }

    // 使用StringBuilder进行高效拼接
    public static String buildReport(java.util.List<String> testResults) {
        StringBuilder report = new StringBuilder();
        report.append("测试报告n");
        report.append("=" .repeat(20)).append("n");

        for (int i = 0; i < testResults.size(); i++) {
            report.append(String.format("%d. %sn", i + 1, testResults.get(i)));
        }

        return report.toString();
    }

    public static void main(String[] args) {
        // 测试字符串拼接功能
        String[] keywords = {"Selenium", "WebDriver", "自动化"};
        System.out.println("搜索查询: " + buildSearchQuery(keywords));

        String filePath = buildFilePath("C:", "Users", "test", "reports", "result.txt");
        System.out.println("文件路径: " + filePath);

        java.util.Map<String, String> params = new java.util.HashMap<>();
        params.put("page", "1");
        params.put("size", "10");
        params.put("sort", "name");
        System.out.println("URL参数: " + buildUrlParams(params));

        java.util.List<String> results = java.util.Arrays.asList(
            "登录测试 - 通过",
            "搜索测试 - 通过", 
            "购买测试 - 失败"
        );
        System.out.println(buildReport(results));
    }
}

7. 处理特殊字符和编码

7.1 处理HTML实体和特殊字符

import java.net.URLEncoder;
import java.net.URLDecoder;
import java.nio.charset.StandardCharsets;

public class SpecialCharacterHandling {

    // 处理HTML实体
    public static String decodeHtmlEntities(String text) {
        return text.replace("&amp;", "&")
                  .replace("&lt;", "<")
                  .replace("&gt;", ">")
                  .replace("&quot;", """)
                  .replace("&#39;", "'")
                  .replace("&nbsp;", " ");
    }

    // URL编码
    public static String urlEncode(String text) {
        try {
            return URLEncoder.encode(text, StandardCharsets.UTF_8.toString());
        } catch (Exception e) {
            return text;
        }
    }

    // URL解码
    public static String urlDecode(String text) {
        try {
            return URLDecoder.decode(text, StandardCharsets.UTF_8.toString());
        } catch (Exception e) {
            return text;
        }
    }

    // 移除不可见字符
    public static String removeInvisibleChars(String text) {
        // 移除零宽字符和其他不可见字符
        return text.replaceAll("[\u200B-\u200D\uFEFF]", "")
                  .replaceAll("[\u0000-\u001F\u007F]", "");
    }

    // 处理换行符
    public static String normalizeLineBreaks(String text) {
        return text.replaceAll("\r\n|\r|\n", System.lineSeparator());
    }

    public static void main(String[] args) {
        WebDriver driver = new ChromeDriver();

        try {
            driver.get("https://example.com");

            // 获取可能包含HTML实体的文本
            WebElement element = driver.findElement(By.className("content"));
            String rawText = element.getText();

            // 处理特殊字符
            String cleanText = decodeHtmlEntities(rawText);
            cleanText = removeInvisibleChars(cleanText);
            cleanText = normalizeLineBreaks(cleanText);

            System.out.println("原始文本: " + rawText);
            System.out.println("清理后文本: " + cleanText);

            // URL编码示例
            String searchTerm = "Java 自动化测试";
            String encodedTerm = urlEncode(searchTerm);
            System.out.println("编码前: " + searchTerm);
            System.out.println("编码后: " + encodedTerm);
            System.out.println("解码后: " + urlDecode(encodedTerm));

        } finally {
            driver.quit();
        }
    }
}

7.2 处理不同编码格式

import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets;

public class EncodingHandling {

    // 检测字符串编码
    public static void detectEncoding(String text) {
        byte[] bytes = text.getBytes();

        // 尝试不同编码
        String[] encodings = {"UTF-8", "GBK", "GB2312", "ISO-8859-1"};

        for (String encoding : encodings) {
            try {
                Charset charset = Charset.forName(encoding);
                String decoded = new String(bytes, charset);
                System.out.println(encoding + ": " + decoded);
            } catch (Exception e) {
                System.out.println(encoding + ": 解码失败");
            }
        }
    }

    // 转换编码
    public static String convertEncoding(String text, String fromEncoding, String toEncoding) {
        try {
            byte[] bytes = text.getBytes(fromEncoding);
            return new String(bytes, toEncoding);
        } catch (Exception e) {
            System.out.println("编码转换失败: " + e.getMessage());
            return text;
        }
    }

    // 确保UTF-8编码
    public static String ensureUtf8(String text) {
        return new String(text.getBytes(StandardCharsets.UTF_8), StandardCharsets.UTF_8);
    }
}

8. 实际应用场景

8.1 电商网站价格比较

public class PriceComparisonExample {

    public static class Product {
        private String name;
        private double price;
        private String currency;

        public Product(String name, double price, String currency) {
            this.name = name;
            this.price = price;
            this.currency = currency;
        }

        // getters and setters
        public String getName() { return name; }
        public double getPrice() { return price; }
        public String getCurrency() { return currency; }
    }

    // 从价格文本中提取价格信息
    public static Product extractProductInfo(WebElement productElement) {
        String nameText = productElement.findElement(By.className("product-name")).getText();
        String priceText = productElement.findElement(By.className("price")).getText();

        // 解析价格 - 支持多种格式: "¥299.99", "$29.99", "€25.50"
        String currency = "";
        double price = 0.0;

        if (priceText.startsWith("¥")) {
            currency = "CNY";
            price = Double.parseDouble(priceText.substring(1));
        } else if (priceText.startsWith("$")) {
            currency = "USD";
            price = Double.parseDouble(priceText.substring(1));
        } else if (priceText.startsWith("€")) {
            currency = "EUR";
            price = Double.parseDouble(priceText.substring(1));
        }

        return new Product(nameText.trim(), price, currency);
    }

    // 比较产品价格
    public static void compareProducts(WebDriver driver) {
        java.util.List<WebElement> productElements = 
            driver.findElements(By.className("product-item"));

        java.util.List<Product> products = new java.util.ArrayList<>();

        for (WebElement element : productElements) {
            Product product = extractProductInfo(element);
            products.add(product);
        }

        // 按价格排序
        products.sort((p1, p2) -> Double.compare(p1.getPrice(), p2.getPrice()));

        System.out.println("产品价格排序(从低到高):");
        for (Product product : products) {
            System.out.printf("%-30s %s%.2fn", 
                product.getName(), 
                getCurrencySymbol(product.getCurrency()), 
                product.getPrice());
        }
    }

    private static String getCurrencySymbol(String currency) {
        switch (currency) {
            case "CNY": return "¥";
            case "USD": return "$";
            case "EUR": return "€";
            default: return "";
        }
    }
}

8.2 表单数据验证

public class FormValidationExample {

    // 验证用户注册表单
    public static void validateRegistrationForm(WebDriver driver) {
        // 获取表单字段
        WebElement usernameField = driver.findElement(By.id("username"));
        WebElement emailField = driver.findElement(By.id("email"));
        WebElement phoneField = driver.findElement(By.id("phone"));
        WebElement passwordField = driver.findElement(By.id("password"));

        // 获取字段值
        String username = usernameField.getAttribute("value");
        String email = emailField.getAttribute("value");
        String phone = phoneField.getAttribute("value");
        String password = passwordField.getAttribute("value");

        // 验证用户名
        if (!isValidUsername(username)) {
            System.out.println("用户名格式不正确: " + username);
        }

        // 验证邮箱
        if (!isValidEmail(email)) {
            System.out.println("邮箱格式不正确: " + email);
        }

        // 验证手机号
        if (!isValidChinesePhone(phone)) {
            System.out.println("手机号格式不正确: " + phone);
        }

        // 验证密码强度
        if (!isStrongPassword(password)) {
            System.out.println("密码强度不够: " + getPasswordStrengthMessage(password));
        }
    }

    // 验证用户名格式
    public static boolean isValidUsername(String username) {
        // 用户名:3-20个字符,只能包含字母、数字、下划线
        String usernameRegex = "^[a-zA-Z0-9_]{3,20}$";
        return username != null && username.matches(usernameRegex);
    }

    // 验证密码强度
    public static boolean isStrongPassword(String password) {
        if (password == null || password.length() < 8) {
            return false;
        }

        // 至少包含一个大写字母、一个小写字母、一个数字、一个特殊字符
        boolean hasUpper = password.matches(".*[A-Z].*");
        boolean hasLower = password.matches(".*[a-z].*");
        boolean hasDigit = password.matches(".*\d.*");
        boolean hasSpecial = password.matches(".*[!@#$%^&*()_+\-=\[\]{};':"\\|,.<>\/?].*");

        return hasUpper && hasLower && hasDigit && hasSpecial;
    }

    // 获取密码强度提示信息
    public static String getPasswordStrengthMessage(String password) {
        java.util.List<String> requirements = new java.util.ArrayList<>();

        if (password == null || password.length() < 8) {
            requirements.add("至少8个字符");
        }
        if (!password.matches(".*[A-Z].*")) {
            requirements.add("至少一个大写字母");
        }
        if (!password.matches(".*[a-z].*")) {
            requirements.add("至少一个小写字母");
        }
        if (!password.matches(".*\d.*")) {
            requirements.add("至少一个数字");
        }
        if (!password.matches(".*[!@#$%^&*()_+\-=\[\]{};':"\\|,.<>\/?].*")) {
            requirements.add("至少一个特殊字符");
        }

        return "密码需要包含: " + String.join(", ", requirements);
    }
}

8.3 日志分析和报告生成

import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;

public class LogAnalysisExample {

    public static class TestResult {
        private String testName;
        private String status;
        private String message;
        private LocalDateTime timestamp;

        public TestResult(String testName, String status, String message) {
            this.testName = testName;
            this.status = status;
            this.message = message;
            this.timestamp = LocalDateTime.now();
        }

        // getters
        public String getTestName() { return testName; }
        public String getStatus() { return status; }
        public String getMessage() { return message; }
        public LocalDateTime getTimestamp() { return timestamp; }
    }

    // 收集测试结果
    public static java.util.List<TestResult> collectTestResults(WebDriver driver) {
        java.util.List<TestResult> results = new java.util.ArrayList<>();

        // 假设页面上有测试结果表格
        java.util.List<WebElement> resultRows = 
            driver.findElements(By.cssSelector(".test-results tbody tr"));

        for (WebElement row : resultRows) {
            java.util.List<WebElement> cells = row.findElements(By.tagName("td"));
            if (cells.size() >= 3) {
                String testName = cells.get(0).getText().trim();
                String status = cells.get(1).getText().trim();
                String message = cells.get(2).getText().trim();

                results.add(new TestResult(testName, status, message));
            }
        }

        return results;
    }

    // 生成测试报告
    public static String generateTestReport(java.util.List<TestResult> results) {
        StringBuilder report = new StringBuilder();
        DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");

        // 报告头部
        report.append("自动化测试报告n");
        report.append("=".repeat(50)).append("n");
        report.append("生成时间: ").append(LocalDateTime.now().format(formatter)).append("nn");

        // 统计信息
        long passedCount = results.stream().filter(r -> "PASS".equals(r.getStatus())).count();
        long failedCount = results.stream().filter(r -> "FAIL".equals(r.getStatus())).count();
        long skippedCount = results.stream().filter(r -> "SKIP".equals(r.getStatus())).count();

        report.append("测试统计:n");
        report.append(String.format("  总计: %dn", results.size()));
        report.append(String.format("  通过: %dn", passedCount));
        report.append(String.format("  失败: %dn", failedCount));
        report.append(String.format("  跳过: %dn", skippedCount));
        report.append(String.format("  成功率: %.1f%%nn", 
            (double) passedCount / results.size() * 100));

        // 详细结果
        report.append("详细结果:n");
        report.append("-".repeat(50)).append("n");

        for (TestResult result : results) {
            report.append(String.format("%-30s [%s] %sn", 
                result.getTestName(), 
                result.getStatus(), 
                result.getMessage()));
        }

        // 失败测试汇总
        java.util.List<TestResult> failedTests = results.stream()
            .filter(r -> "FAIL".equals(r.getStatus()))
            .collect(java.util.stream.Collectors.toList());

        if (!failedTests.isEmpty()) {
            report.append("n失败测试详情:n");
            report.append("-".repeat(50)).append("n");

            for (TestResult failed : failedTests) {
                report.append(String.format("测试名称: %sn", failed.getTestName()));
                report.append(String.format("失败原因: %sn", failed.getMessage()));
                report.append(String.format("时间: %snn", 
                    failed.getTimestamp().format(formatter)));
            }
        }

        return report.toString();
    }
}

9. 最佳实践和注意事项

9.1 性能优化

public class StringPerformanceOptimization {

    // 使用StringBuilder进行大量字符串拼接
    public static String efficientStringBuilding(java.util.List<String> items) {
        StringBuilder sb = new StringBuilder();
        for (String item : items) {
            sb.append(item).append("n");
        }
        return sb.toString();
    }

    // 避免在循环中进行字符串拼接
    public static String inefficientWay(java.util.List<String> items) {
        String result = "";
        for (String item : items) {
            result += item + "n"; // 每次都创建新的String对象
        }
        return result;
    }

    // 使用String.format进行格式化
    public static String formatMessage(String name, int count, double price) {
        // 推荐方式
        return String.format("用户 %s 购买了 %d 件商品,总价 %.2f 元", name, count, price);

        // 不推荐的拼接方式
        // return "用户 " + name + " 购买了 " + count + " 件商品,总价 " + price + " 元";
    }

    // 缓存编译的正则表达式
    private static final Pattern EMAIL_PATTERN = 
        Pattern.compile("^[A-Za-z0-9+_.-]+@([A-Za-z0-9.-]+\.[A-Za-z]{2,})$");

    public static boolean isValidEmailCached(String email) {
        return EMAIL_PATTERN.matcher(email).matches();
    }
}

9.2 错误处理和异常管理

public class StringErrorHandling {

    // 安全的字符串操作
    public static String safeSubstring(String str, int start, int end) {
        if (str == null) {
            return "";
        }

        int length = str.length();
        start = Math.max(0, Math.min(start, length));
        end = Math.max(start, Math.min(end, length));

        return str.substring(start, end);
    }

    // 安全的数字转换
    public static double safeParseDouble(String str, double defaultValue) {
        if (str == null || str.trim().isEmpty()) {
            return defaultValue;
        }

        try {
            return Double.parseDouble(str.trim());
        } catch (NumberFormatException e) {
            System.out.println("数字解析失败: " + str + ", 使用默认值: " + defaultValue);
            return defaultValue;
        }
    }

    // 安全的元素文本获取
    public static String safeGetText(WebElement element) {
        try {
            return element != null ? element.getText().trim() : "";
        } catch (Exception e) {
            System.out.println("获取元素文本失败: " + e.getMessage());
            return "";
        }
    }

    // 带重试的文本获取
    public static String getTextWithRetry(WebElement element, int maxRetries) {
        for (int i = 0; i < maxRetries; i++) {
            try {
                String text = element.getText();
                if (text != null && !text.trim().isEmpty()) {
                    return text.trim();
                }
            } catch (Exception e) {
                System.out.println("第 " + (i + 1) + " 次获取文本失败: " + e.getMessage());
                if (i < maxRetries - 1) {
                    try {
                        Thread.sleep(1000); // 等待1秒后重试
                    } catch (InterruptedException ie) {
                        Thread.currentThread().interrupt();
                        break;
                    }
                }
            }
        }
        return "";
    }
}

9.3 代码组织和可维护性

// 字符串常量管理
public class StringConstants {
    // 正则表达式常量
    public static final String EMAIL_REGEX = "^[A-Za-z0-9+_.-]+@([A-Za-z0-9.-]+\.[A-Za-z]{2,})$";
    public static final String PHONE_REGEX = "^1[3-9]\d{9}$";
    public static final String PASSWORD_REGEX = "^(?=.*[a-z])(?=.*[A-Z])(?=.*\d)(?=.*[@$!%*?&])[A-Za-z\d@$!%*?&]{8,}$";

    // 错误消息常量
    public static final String INVALID_EMAIL_MSG = "邮箱格式不正确";
    public static final String INVALID_PHONE_MSG = "手机号格式不正确";
    public static final String WEAK_PASSWORD_MSG = "密码强度不够";

    // 格式化模板
    public static final String PRICE_FORMAT = "¥%.2f";
    public static final String PERCENTAGE_FORMAT = "%.1f%%";
    public static final String DATE_FORMAT = "yyyy-MM-dd HH:mm:ss";
}

// 字符串工具类
public class StringUtils {

    // 私有构造函数防止实例化
    private StringUtils() {}

    // 检查字符串是否为空
    public static boolean isEmpty(String str) {
        return str == null || str.trim().isEmpty();
    }

    // 检查字符串是否不为空
    public static boolean isNotEmpty(String str) {
        return !isEmpty(str);
    }

    // 安全的字符串比较
    public static boolean equals(String str1, String str2) {
        return str1 == null ? str2 == null : str1.equals(str2);
    }

    // 忽略大小写的安全比较
    public static boolean equalsIgnoreCase(String str1, String str2) {
        return str1 == null ? str2 == null : str1.equalsIgnoreCase(str2);
    }

    // 截断字符串
    public static String truncate(String str, int maxLength) {
        if (isEmpty(str) || str.length() <= maxLength) {
            return str;
        }
        return str.substring(0, maxLength) + "...";
    }

    // 首字母大写
    public static String capitalize(String str) {
        if (isEmpty(str)) {
            return str;
        }
        return str.substring(0, 1).toUpperCase() + str.substring(1).toLowerCase();
    }
}

9.4 测试和调试技巧

public class StringDebuggingTips {

    // 调试字符串内容
    public static void debugString(String str, String label) {
        System.out.println("=== " + label + " ===");
        System.out.println("原始: '" + str + "'");
        System.out.println("长度: " + (str != null ? str.length() : "null"));

        if (str != null) {
            System.out.println("去空格: '" + str.trim() + "'");
            System.out.println("去空格长度: " + str.trim().length());

            // 显示不可见字符
            StringBuilder visible = new StringBuilder();
            for (char c : str.toCharArray()) {
                if (Character.isISOControl(c)) {
                    visible.append("\u").append(String.format("%04X", (int) c));
                } else {
                    visible.append(c);
                }
            }
            System.out.println("可见字符: '" + visible.toString() + "'");
        }
        System.out.println("================n");
    }

    // 比较两个字符串的差异
    public static void compareStrings(String str1, String str2) {
        System.out.println("字符串比较:");
        System.out.println("字符串1: '" + str1 + "'");
        System.out.println("字符串2: '" + str2 + "'");
        System.out.println("相等: " + Objects.equals(str1, str2));
        System.out.println("忽略大小写相等: " + 
            (str1 != null && str2 != null && str1.equalsIgnoreCase(str2)));

        if (str1 != null && str2 != null) {
            System.out.println("长度1: " + str1.length());
            System.out.println("长度2: " + str2.length());

            // 找出第一个不同的字符位置
            int minLength = Math.min(str1.length(), str2.length());
            for (int i = 0; i < minLength; i++) {
                if (str1.charAt(i) != str2.charAt(i)) {
                    System.out.println("第一个差异位置: " + i);
                    System.out.println("字符1: '" + str1.charAt(i) + "' (Unicode: " + 
                        (int) str1.charAt(i) + ")");
                    System.out.println("字符2: '" + str2.charAt(i) + "' (Unicode: " + 
                        (int) str2.charAt(i) + ")");
                    break;
                }
            }
        }
        System.out.println();
    }

    // 记录字符串操作日志
    public static String logStringOperation(String operation, String input, String output) {
        String logMessage = String.format("[%s] %s: '%s' -> '%s'", 
            LocalDateTime.now().format(DateTimeFormatter.ofPattern("HH:mm:ss")),
            operation, input, output);
        System.out.println(logMessage);
        return output;
    }
}

总结

字符串操作在Java+Selenium自动化测试中扮演着重要角色。掌握这些技能可以帮助我们:

  1. 高效处理页面数据:从网页元素中提取、验证和转换文本信息
  2. 提高测试可靠性:通过正确的字符串比较和验证确保测试准确性
  3. 优化代码性能:使用合适的字符串操作方法避免性能问题
  4. 增强代码可维护性:通过工具类和常量管理提高代码质量

关键要点:

  • 始终考虑null值和空字符串的处理
  • 使用StringBuilder进行大量字符串拼接
  • 缓存编译的正则表达式以提高性能
  • 合理使用字符串工具类和常量管理
  • 在调试时充分利用字符串分析工具

通过掌握这些字符串操作技巧,您可以编写更加健壮和高效的自动化测试代码。

发表评论