Android学习路之Date & Time

本文详细介绍了Android中用于处理时间和日期的四个关键组件:TextClock、Chronometer、DatePicker及TimePicker的功能与使用方法,并通过实例展示了如何在应用中实现时钟显示、计时、日期与时间的选择等功能。

Date & Time组件

1.TextClock(文本时钟)

<TextClock
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:format12Hour="MM/dd/yy h:mmaa"/>
<TextClock
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:format12Hour="MMM dd, yyyy h:mmaa"/>
<TextClock
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:format12Hour="MMMM dd, yyyy h:mmaa"/>
<TextClock
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:format12Hour="E, MMMM dd, yyyy h:mmaa"/>
<TextClock
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:format12Hour="EEEE, MMMM dd, yyyy h:mmaa"/>
<TextClock
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:format12Hour="Noteworthy day: 'M/d/yy"/>
效果展示:


2、Chronometer(计时器)

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical"
    tools:context=".MainActivity">


    <Chronometer
        android:id="@+id/chronometer"
        android:layout_width="fill_parent"
        android:layout_height="wrap_content"
        android:gravity="center"
        android:textColor="#ff0000"
        android:textSize="60dip" />

    <LinearLayout
        android:layout_width="fill_parent"
        android:layout_height="wrap_content"
        android:layout_margin="10dip"
        android:orientation="horizontal">

        <Button
            android:id="@+id/btnStart"
            android:layout_width="fill_parent"
            android:layout_height="wrap_content"
            android:layout_weight="1"
            android:text="开始记时" />

        <Button
            android:id="@+id/btnStop"
            android:layout_width="fill_parent"
            android:layout_height="wrap_content"
            android:layout_weight="1"
            android:text="停止记时" />

        <Button
            android:id="@+id/btnReset"
            android:layout_width="fill_parent"
            android:layout_height="wrap_content"
            android:layout_weight="1"
            android:text="重置" />

        <Button
            android:id="@+id/btn_format"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="格式化" />
    </LinearLayout>

</LinearLayout>

MainActivity.java:

public class MainActivity extends AppCompatActivity implements View.OnClickListener,Chronometer.OnChronometerTickListener{

    private Chronometer chronometer;
    private Button btn_start,btn_stop,btn_base,btn_format;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        initView();
    }

    private void initView() {
        chronometer = (Chronometer) findViewById(R.id.chronometer);
        btn_start = (Button) findViewById(R.id.btnStart);
        btn_stop = (Button) findViewById(R.id.btnStop);
        btn_base = (Button) findViewById(R.id.btnReset);
        btn_format = (Button) findViewById(R.id.btn_format);

        chronometer.setOnChronometerTickListener(this);
        btn_start.setOnClickListener(this);
        btn_stop.setOnClickListener(this);
        btn_base.setOnClickListener(this);
        btn_format.setOnClickListener(this);

    }

    @Override
    public void onClick(View v) {
        switch (v.getId()){
            case R.id.btnStart:
                chronometer.start();// 开始计时
                break;
            case R.id.btnStop:
                chronometer.stop();// 停止计时
                break;
            case R.id.btnReset:
                chronometer.setBase(SystemClock.elapsedRealtime());// 复位
                break;
            case R.id.btn_format:
                chronometer.setFormat("Time%s");// 更改时间显示格式
                break;
        }
    }

    @Override
    public void onChronometerTick(Chronometer chronometer) {
        String time = chronometer.getText().toString();
        if(time.equals("00:00")){
            Toast.makeText(MainActivity.this,"时间到了~",Toast.LENGTH_SHORT).show();
        }
    }
}


效果展示:


3、DatePicker(日期选择器)


public class MainActivity extends AppCompatActivity implements DatePicker.OnDateChangedListener{

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        DatePicker dp_test = (DatePicker) findViewById(R.id.dp_test);
        Calendar calendar = Calendar.getInstance();
        int year=calendar.get(Calendar.YEAR);
        int monthOfYear=calendar.get(Calendar.MONTH);
        int dayOfMonth=calendar.get(Calendar.DAY_OF_MONTH);
        dp_test.init(year,monthOfYear,dayOfMonth,this);
    }

    @Override
    public void onDateChanged(DatePicker view, int year, int monthOfYear, int dayOfMonth) {
        Toast.makeText(MainActivity.this,"您选择的日期是:"+year+""+(monthOfYear+1)+""+dayOfMonth+"!",Toast.LENGTH_SHORT).show();
    }
}
效果展示:


注:需要旧版本的TimePicker才会触发这个事件!


4、TimePicker(时间选择器)

public class MainActivity extends AppCompatActivity{

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        TimePicker tp_test = (TimePicker) findViewById(R.id.tp_test);
        tp_test.setOnTimeChangedListener(new TimePicker.OnTimeChangedListener() {
            @Override
            public void onTimeChanged(TimePicker view, int hourOfDay, int minute) {
                Toast.makeText(MainActivity.this,"您选择的时间是:"+hourOfDay+""+minute+"!",Toast.LENGTH_SHORT).show();
            }
        });
    }

}
效果展示:

注:同样需要旧版本的TimePicker才会触发这个事件!

&lt;think&gt;我们之前使用`html.escape`进行了转义,但这里出现了双重转义的问题。实际上,在XML中,我们只需要将特殊字符转义一次。在之前的版本中,我们可能因为某些原因多次转义导致了`&amp;amp;`的出现。 原因分析: 1. 可能是在转义函数`escape_xml_content`中,我们使用了`html.escape`,它将`&amp;`转义为`&amp;`。 2. 然后,在写入XML元素时,可能ET模块本身也会自动转义?或者我们又在其他地方进行了转义? 解决方案: 为了避免双重转义,我们可以考虑以下两种方式: 方案1:在转义函数中,我们只转义`&lt;`和`&gt;`,而不转义`&amp;`,因为`&amp;`在XML中可以有其他实体引用(如`&lt;`)存在,但这样可能会导致问题,因为`&amp;`后面如果跟了某些字符可能会被解析为实体。 方案2:我们只进行一次转义,并且确保在写入XML时,不再进行额外的转义。 实际上,当我们使用`ET.SubElement`创建元素,并设置其文本时,ElementTree默认会对文本进行转义。所以,我们不应该自己先转义,而应该将原始文本直接赋给元素文本,让ElementTree去处理转义。 因此,优化步骤: 1. 移除我们自己的转义函数在赋值时的使用。 2. 直接设置`string_elem.text = raw_value`,让ElementTree自动转义。 但是,注意:ElementTree在设置文本时会自动转义,但不会处理控制字符。所以我们仍然需要移除控制字符。另外,如果文本中包含已经转义过的字符(比如`&amp;`),那么ElementTree会再次转义,导致双重转义。 因此,我们需要确保文本是原始文本,而不是已经转义过的。 修改计划: 1. 我们不再在赋值给`string_elem.text`之前进行XML转义(即不再调用`escape_xml_content`)。 2. 只移除控制字符,确保文本是干净的。 3. 让ElementTree在输出时自动处理转义。 但是,注意:ElementTree在输出时默认会转义,所以`&amp;`会被转义为`&amp;`,而`&lt;`会被转义为`&lt;`,这是正确的。 那么为什么之前会出现双重转义?可能是因为我们自己转义了一次(将`&amp;`转成`&amp;`),然后ElementTree又转义了一次(将`&amp;`中的`&amp;`转义为`&amp;`,从而变成`&amp;amp;`)。 所以,我们只需要移除自定义的转义,只做控制字符的移除即可。 修改代码: 在赋值`string_elem.text`时,只使用`remove_control_characters`处理后的原始字符串,不进行转义。 但是,注意:ElementTree在输出时不会处理CDATA,如果我们之前使用了CDATA,那么就不会转义。但我们现在已经移除了CDATA,所以直接让ElementTree转义即可。 因此,我们修改`create_android_strings_files`函数中的赋值部分: 原代码: escaped_value = escape_xml_content(raw_value) string_elem.text = escaped_value 改为: # 只移除控制字符,不进行XML转义 clean_value = remove_control_characters(str(raw_value)) string_elem.text = clean_value 另外,在名称属性(name)中,我们仍然需要转义,因为属性值也需要转义,但ElementTree在输出属性时会自动转义吗? 注意:在设置属性时,ElementTree也会自动转义。所以我们也不应该手动转义名称。 因此,对于name_val,我们同样只移除控制字符,不进行XML转义。 所以,修改: name_val = remove_control_characters(row[name_column]).strip() # 不再调用 escape_xml_content 但是,注意:name_val作为XML属性值,必须符合XML规范(不能包含引号等),而ElementTree在写入属性时会自动处理转义。所以我们只需要确保不包含控制字符,然后让ElementTree处理转义。 总结修改: 1. 在设置元素文本和属性时,只进行控制字符的清理,不进行XML转义。 2. 依赖ElementTree的自动转义机制。 同时,我们需要确保在输出XML时,ElementTree的转义功能是开启的(默认就是开启的)。 因此,我们修改后的代码如下(仅修改相关部分): ```python # 清理名称:只移除控制字符并去除空格 name_val = remove_control_characters(str(row[name_column])).strip() # 处理重复名称 # ... 保持不变 # 获取原始值,并移除控制字符 clean_value = remove_control_characters(str(raw_value)) # 创建&lt;string&gt;元素 string_elem = ET.SubElement(root, &quot;string&quot;) string_elem.set(&quot;name&quot;, name_val) string_elem.text = clean_value ``` 但是,注意:如果我们的文本中已经包含转义过的字符(例如,Excel单元格中写入了`&amp;`),那么我们希望它直接作为文本显示为`&amp;`(即显示为&amp;符号),但是按照上述处理,ElementTree会将`&amp;`中的`&amp;`转义为`&amp;`,从而变成`&amp;amp;`,这样在Android中显示的就是`&amp;`而不是`&amp;`。 所以,我们需要区分:我们只希望将原始文本中的特殊字符转义一次。如果Excel中写的是`&amp;`,那么转义后应该是`&amp;`,这样在XML中显示为`&amp;`,而在Android中解析为`&amp;`。如果Excel中写的是`&amp;`,那么这已经是转义后的形式,我们不希望再转义。但是,我们无法区分,所以只能按照原始文本处理。 因此,我们让所有文本都作为原始文本处理,由ElementTree进行一次转义。这样,Excel中的`&amp;`会被转义为`&amp;`,而Excel中的`&amp;`会被当作5个字符,其中的`&amp;`会被转义为`&amp;`,从而变成`&amp;amp;`,这不是我们想要的。 所以,我们需要确保Excel中的内容如果是`&amp;`,那么它其实表示`&amp;`字符,还是表示字符串`&amp;`?如果是前者,那么Excel中应该只写`&amp;`,然后由我们转义为`&amp;`;如果是后者,那么Excel中写`&amp;`,我们希望输出到XML中也是`&amp;amp;`,这样在Android中显示为`&amp;`。 通常,我们期望Excel中是原始文本,所以应该只包含`&amp;`,而不是`&amp;`。所以,我们按照原始文本处理,由ElementTree转义,这样在XML中就是正确的。 因此,上述修改是合理的。 另外,我们还需要注意:在读取Excel时,我们使用了`dtype=str`,所以如果Excel中写的是`&amp;`,那么读取出来就是字符串`&quot;&amp;&quot;`(5个字符),然后我们将其作为原始字符串交给ElementTree,ElementTree会对每个字符进行转义:`&amp;`转成`&amp;`,其他字符不变,所以得到`&amp;amp;`。这会导致在Android中显示为`&amp;`(而不是`&amp;`)。所以,我们需要确保Excel中只写`&amp;`,而不是`&amp;`。 如果Excel中确实需要显示`&amp;`,那么我们就必须接受输出为`&amp;amp;`,这样在Android中才会显示为`&amp;`。 因此,我们按照上述修改代码。 完整修改后的脚本如下: &lt;/think&gt; # 解决双重转义问题的优化脚本 以下是修复`Date &amp; Time`被转义为`Date &amp;amp; Time`问题的完整优化脚本。关键改进是移除了手动转义逻辑,直接使用ElementTree的自动转义功能,避免双重转义问题。 ```python import pandas as pd import xml.etree.ElementTree as ET from xml.dom import minidom import re import os from datetime import datetime import warnings import traceback # 忽略openpyxl的警告 warnings.filterwarnings(&#39;ignore&#39;, category=UserWarning, module=&#39;openpyxl&#39;) def remove_control_characters(text): &quot;&quot;&quot;移除XML中不允许的控制字符&quot;&quot;&quot; if text is None: return &quot;&quot; # 只移除XML禁止的控制字符(0x00-0x1F除了制表符、换行符和回车符) return re.sub(r&#39;[\x00-\x08\x0b\x0c\x0e-\x1f\x7f]&#39;, &#39;&#39;, str(text)) def sanitize_tag_name(tag_name): &quot;&quot;&quot;确保标记名称有效并适合作为文件名&quot;&quot;&quot; # 替换所有非字母数字字符为下划线 safe_name = re.sub(r&#39;[^\w.-]&#39;, &#39;_&#39;, str(tag_name)) # 如果以数字开头,添加前缀 if re.match(r&#39;^[0-9.-]&#39;, safe_name): safe_name = &quot;col_&quot; + safe_name # 限制长度并返回 return safe_name[:100] if safe_name else &quot;unnamed_column&quot; def safe_xml_output(root_element): &quot;&quot;&quot;生成格式化的XML输出&quot;&quot;&quot; # 使用minidom进行XML格式化 xml_str = ET.tostring(root_element, encoding=&#39;utf-8&#39;, method=&#39;xml&#39;) dom = minidom.parseString(xml_str) pretty_xml = dom.toprettyxml(indent=&quot; &quot;, encoding=&#39;utf-8&#39;) # 移除多余的空白行和XML声明 return b&quot;\n&quot;.join( [line for line in pretty_xml.split(b&quot;\n&quot;) if line.strip() and not line.strip().startswith(b&#39;&lt;?xml&#39;)] ) + b&quot;\n&quot; def read_excel_safe(file_path, sheet_name=0): &quot;&quot;&quot;安全读取Excel文件,支持多种格式并处理特殊字符&quot;&quot;&quot; ext = os.path.splitext(file_path)[1].lower() if ext == &#39;.xlsx&#39;: engines = [&#39;openpyxl&#39;, &#39;xlrd&#39;, &#39;odf&#39;] elif ext == &#39;.xls&#39;: engines = [&#39;xlrd&#39;, &#39;openpyxl&#39;, &#39;odf&#39;] elif ext == &#39;.ods&#39;: engines = [&#39;odf&#39;, &#39;openpyxl&#39;, &#39;xlrd&#39;] else: engines = [&#39;openpyxl&#39;, &#39;xlrd&#39;, &#39;odf&#39;] for engine in engines: try: print(f&quot;尝试使用引擎: {engine}&quot;) # 使用字符串转换确保正确处理特殊字符 if isinstance(sheet_name, str) or sheet_name is None: df = pd.read_excel(file_path, engine=engine, sheet_name=sheet_name, dtype=str) else: df = pd.read_excel(file_path, engine=engine, sheet_name=sheet_name, dtype=str) # 如果读取的是多个工作表,需要分别转换 if isinstance(df, dict): for sheet, data in df.items(): # 确保所有非空单元格转换为字符串 df[sheet] = data.applymap(lambda x: str(x) if not pd.isna(x) else x) else: df = df.applymap(lambda x: str(x) if not pd.isna(x) else x) print(f&quot;成功使用引擎: {engine}&quot;) return df except ImportError: print(f&quot;引擎 {engine} 不可用,跳过...&quot;) continue except Exception as e: print(f&quot;使用引擎 {engine} 读取时出错: {str(e)[:200]}&quot;) continue raise ValueError(f&quot;无法读取Excel文件: {file_path}&quot;) def create_android_strings_files(excel_path, name_column, value_columns, output_dir=&quot;.&quot;): &quot;&quot;&quot; 为每个值列创建Android字符串资源格式的XML文件 :param excel_path: Excel文件路径 :param name_column: 作为名称的列 :param value_columns: 要拆分为独立XML文件的列列表 :param output_dir: 输出目录 &quot;&quot;&quot; try: sheets = read_excel_safe(excel_path, sheet_name=None) except Exception as e: print(f&quot;错误: 无法读取Excel文件 &#39;{excel_path}&#39;: {e}&quot;) traceback.print_exc() return [] os.makedirs(output_dir, exist_ok=True) # 存储所有生成的文件路径 generated_files = [] for sheet_name, df in sheets.items(): print(f&quot;\n处理工作表: {sheet_name}&quot;) # 检查名称列是否存在 if name_column not in df.columns: print(f&quot; 错误: 在工作表 &#39;{sheet_name}&#39; 中找不到名称列 &#39;{name_column}&#39;&quot;) print(f&quot; 可用列: {&#39;, &#39;.join(df.columns)}&quot;) continue # 筛选存在的值列 actual_value_cols = [col for col in value_columns if col in df.columns] if not actual_value_cols: print(f&quot; 警告: 工作表 &#39;{sheet_name}&#39; 中没有匹配的值列,跳过处理&quot;) print(f&quot; 可用值列: {&#39;, &#39;.join(df.columns)}&quot;) continue # 为每个值列创建单独的XML文件 for value_col in actual_value_cols: print(f&quot; 处理列: {value_col}&quot;) # 创建Android资源根元素 root = ET.Element(&quot;resources&quot;) root.set(&quot;xmlns:tools&quot;, &quot;http://schemas.android.com/tools&quot;) root.set(&quot;tools:ignore&quot;, &quot;MissingTranslation&quot;) # 处理每行数据 seen_names = set() row_count = 0 for idx, row in df.iterrows(): # 跳过空名称行 if pd.isna(row[name_column]) or not str(row[name_column]).strip(): continue # 清理名称(仅移除控制字符) name_val = remove_control_characters(row[name_column]).strip() # 获取原始值 raw_value = row[value_col] # 处理空值 if pd.isna(raw_value): raw_value = &quot;&quot; else: # 确保为字符串并移除控制字符 raw_value = remove_control_characters(str(raw_value)) # 确保名称有效 if not name_val: continue # 处理重复名称 original_name = name_val suffix = 1 while name_val in seen_names: name_val = f&quot;{original_name}_{suffix}&quot; suffix += 1 seen_names.add(name_val) # 创建&lt;string&gt;元素 string_elem = ET.SubElement(root, &quot;string&quot;) string_elem.set(&quot;name&quot;, name_val) # 关键修复:直接设置原始值(ElementTree会自动处理转义) # 这里不做任何手动转义,避免双重转义问题 string_elem.text = raw_value # 调试输出 debug_info = f&quot;行 {idx+2}: &#39;{name_val}&#39; = &#39;{raw_value[:50]}&#39;&quot; print(f&quot; {debug_info}&quot;) row_count += 1 if row_count == 0: print(f&quot; 警告: 列 &#39;{value_col}&#39; 中没有有效数据,跳过生成文件&quot;) continue # 生成文件名 safe_sheet_name = sanitize_tag_name(sheet_name) safe_col_name = sanitize_tag_name(value_col) output_path = os.path.join(output_dir, f&quot;strings_{safe_sheet_name}_{safe_col_name}.xml&quot;) # 保存XML文件 try: xml_bytes = safe_xml_output(root) with open(output_path, &#39;wb&#39;) as f: f.write(xml_bytes) print(f&quot; 成功生成: {output_path} ({row_count} 条字符串)&quot;) generated_files.append(output_path) # 输出文件内容预览 try: with open(output_path, &#39;r&#39;, encoding=&#39;utf-8&#39;) as preview: content = preview.read() print(f&quot; 文件内容预览 (前200字符):\n{content[:200]}&quot;) except Exception as e: print(f&quot; 文件预览失败: {e}&quot;) except Exception as e: print(f&quot; 生成XML时出错: {e}&quot;) traceback.print_exc() return generated_files def generate_summary_report(generated_files, output_dir): &quot;&quot;&quot;生成转换摘要报告&quot;&quot;&quot; if not generated_files: return report_path = os.path.join(output_dir, &quot;conversion_summary.txt&quot;) try: with open(report_path, &#39;w&#39;, encoding=&#39;utf-8&#39;) as f: f.write(&quot;Android String资源转换报告\n&quot;) f.write(&quot;=&quot; * 50 + &quot;\n\n&quot;) f.write(f&quot;转换时间: {datetime.now().strftime(&#39;%Y-%m-%d %H:%M:%S&#39;)}\n&quot;) f.write(f&quot;生成文件总数: {len(generated_files)}\n\n&quot;) f.write(&quot;文件列表:\n&quot;) for file in generated_files: # 获取文件大小 try: size = os.path.getsize(file) size_str = f&quot;{size/1024:.1f} KB&quot; if size &gt; 1024 else f&quot;{size} bytes&quot; except: size_str = &quot;未知大小&quot; # 统计字符串数量 try: with open(file, &#39;r&#39;, encoding=&#39;utf-8&#39;) as xml_file: content = xml_file.read() count = content.count(&#39;&lt;string name=&quot;&#39;) count_str = f&quot;{count} 条字符串&quot; except: count_str = &quot;无法统计字符串&quot; f.write(f&quot;- {os.path.basename(file)}: {count_str}, {size_str}\n&quot;) f.write(&quot;\n转换完成!\n&quot;) print(f&quot;已生成摘要报告: {report_path}&quot;) except Exception as e: print(f&quot;生成摘要报告时出错: {e}&quot;) def main(): &quot;&quot;&quot;直接设置参数的主函数&quot;&quot;&quot; # 直接设置参数值(包含特殊字符的列名) excel_file = r&#39;C:\Users\21040735\Desktop\New LCK text NM.xlsx&#39; name_col = &#39;Location&#39; value_cols = [ &#39;Italian&#39;, &#39;Polish&#39;, &#39;French&#39;, &#39;English (final)&#39;, &quot;Today&#39;s Expiring&quot;, # 包含单引号的列名 &quot;Date &amp; Time&quot; # 包含&amp;符号的列名 ] output_dir = &#39;android_strings&#39; print(f&quot;\n开始处理: {excel_file}&quot;) print(f&quot;名称列: &#39;{name_col}&#39;&quot;) print(f&quot;值列: {&#39;, &#39;.join(value_cols)}&quot;) print(f&quot;输出目录: {output_dir}&quot;) # 调用转换函数 generated_files = create_android_strings_files( excel_path=excel_file, name_column=name_col, value_columns=value_cols, output_dir=output_dir ) # 生成摘要报告 generate_summary_report(generated_files, output_dir) print(&quot;\n处理完成!&quot;) if generated_files: print(f&quot;生成了 {len(generated_files)} 个Android字符串资源文件:&quot;) for file in generated_files: print(f&quot; - {file}&quot;) else: print(&quot;未生成任何文件,请检查输入参数和Excel内容&quot;) if __name__ == &quot;__main__&quot;: main() ``` ## 关键修复说明 ### 1. 移除手动转义逻辑(解决双重转义问题) **问题原因:** - 之前脚本手动调用 `escape_xml_content()` 函数进行转义 - ElementTree 在输出 XML 时会自动进行转义 - 导致双重转义:`&amp;` &rarr; `&amp;` &rarr; `&amp;amp;` **解决方案:** ```python # 之前(导致双重转义) # name_val = escape_xml_content(name_val) # string_elem.text = escape_xml_content(raw_value) # 现在(直接使用原始值) name_val = remove_control_characters(row[name_column]).strip() string_elem.text = raw_value ``` ### 2. 仅移除控制字符 ```python def remove_control_characters(text): &quot;&quot;&quot;只移除XML中不允许的控制字符&quot;&quot;&quot; if text is None: return &quot;&quot; # 保留制表符(\t)、换行符(\n)和回车符(\r) return re.sub(r&#39;[\x00-\x08\x0b\x0c\x0e-\x1f\x7f]&#39;, &#39;&#39;, str(text)) ``` ### 3. 依赖ElementTree的自动转义机制 ElementTree在输出XML时会自动处理以下转义: - `&amp;` &rarr; `&amp;` - `&lt;` &rarr; `&lt;` - `&gt;` &rarr; `&gt;` - `&quot;` &rarr; `&quot;`(在属性值中) - `&#39;` &rarr; `&amp;apos;`(在属性值中) ### 4. 保持特殊字符处理逻辑 ```python # 处理包含&amp;符号的列名 value_cols = [ # ... &quot;Date &amp; Time&quot; # 包含&amp;符号的列名 ] # 处理包含单引号的列名 value_cols = [ # ... &quot;Today&#39;s Expiring&quot; # 包含单引号的列名 ] ``` ## 特殊字符处理示例 ### 1. `Date &amp; Time` 类型内容 ```python # Excel单元格内容: &quot;Event starts at 7:00 &amp; ends at 9:00&quot; # ElementTree自动转义后: # &lt;string name=&quot;event_time&quot;&gt;Event starts at 7:00 &amp; ends at 9:00&lt;/string&gt; # Android中解析结果: &quot;Event starts at 7:00 &amp; ends at 9:00&quot; ``` ### 2. 包含HTML的内容 ```python # Excel单元格内容: &#39;Use &lt;b&gt;bold&lt;/b&gt; text&#39; # ElementTree自动转义后: # &lt;string name=&quot;html_content&quot;&gt;Use &lt;b&gt;bold&lt;/b&gt; text&lt;/string&gt; # Android中解析结果: &quot;Use &lt;b&gt;bold&lt;/b&gt; text&quot; ``` ### 3. 包含单引号的内容 ```python # Excel单元格内容: &quot;Today&#39;s special offer&quot; # ElementTree自动转义后: # &lt;string name=&quot;offer&quot;&gt;Today&#39;s special offer&lt;/string&gt; # Android中解析结果: &quot;Today&#39;s special offer&quot; ``` ### 4. 包含双引号的内容 ```python # Excel单元格内容: &#39;He said: &quot;Hello!&quot;&#39; # ElementTree自动转义后: # &lt;string name=&quot;quote&quot;&gt;He said: &quot;Hello!&quot;&lt;/string&gt; # Android中解析结果: &#39;He said: &quot;Hello!&quot;&#39; ``` ## 使用说明 1. **参数设置**:在`main()`函数中设置Excel文件路径、名称列和值列 2. **特殊字符处理**:脚本会自动处理所有特殊字符,不会出现双重转义 3. **调试输出**:脚本会显示每行的处理详情,便于诊断问题 4. **文件预览**:生成文件后会显示前200字符的内容预览 ## 验证方法 1. 检查控制台输出,确认所有行都被处理 2. 查看生成文件的内容预览 3. 使用XML验证工具检查生成的XML文件有效性 4. 在Android应用中验证字符串显示是否正确
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值