第一次提交

This commit is contained in:
taiyi 2025-09-10 10:35:03 +08:00
commit 0c77c42add
4 changed files with 2232 additions and 0 deletions

8
.idea/TxT2DOCX.iml Normal file
View File

@ -0,0 +1,8 @@
<?xml version="1.0" encoding="UTF-8"?>
<module type="PYTHON_MODULE" version="4">
<component name="NewModuleRootManager">
<content url="file://$MODULE_DIR$" />
<orderEntry type="jdk" jdkName="Python 3.10" jdkType="Python SDK" />
<orderEntry type="sourceFolder" forTests="false" />
</component>
</module>

1635
Txt2docx2.py Normal file

File diff suppressed because it is too large Load Diff

116
data/error_chars.json Normal file
View File

@ -0,0 +1,116 @@
{
"日": "曰",
"木": "本",
"度": "渡",
"暴": "爆",
"籍": "藉",
"销": "消",
"璧": "壁",
"讴": "呕",
"勠": "戮",
"篡": "纂",
"需": "须",
"迄": "讫",
"磬": "罄",
"驰": "弛",
"拨": "拔",
"朴": "扑",
"沾": "粘",
"戊": "戌",
"崇": "祟",
"菅": "管",
"荼": "茶",
"灸": "炙",
"钓": "钧",
"丐": "丏",
"亨": "享",
"赢": "羸",
"肓": "盲",
"赝": "膺",
"掣": "擎",
"峰": "锋",
"读": "续",
"眯": "咪",
"胶": "狡",
"旯": "旮",
"奄": "掩",
"恃": "持",
"径": "胫",
"坝": "狈",
"幅": "副",
"颗": "棵",
"即": "既",
"俩": "两",
"辨": "辩",
"树立": "竖立",
"其他": "其它",
"截止": "截至",
"考查": "考察",
"治服": "制服",
"权利": "权力",
"申明": "声明",
"交代": "交待",
"含义": "涵义",
"安": "按",
"曝": "暴",
"博": "搏",
"灿": "粲",
"毫": "豪",
"检": "捡",
"骄": "娇",
"梁": "粱",
"蓬": "篷",
"辟": "僻",
"欺": "期",
"洽": "恰",
"皱": "邹",
"诸": "著",
"煮": "著",
"壮": "状",
"追": "摧",
"卓": "桌",
"咨": "资",
"滋": "磁",
"阻": "组",
"遵": "尊",
"的": "得",
"她": "他",
"到": "倒",
"倒": "到",
"要": "耍",
"说": "讲",
"讲": "说",
"做": "作",
"作": "做",
"已": "已",
"己": "已",
"以": "已",
"进": "近",
"近": "进",
"象": "像",
"像": "象",
"茶": "荼",
"孑": "孓",
"子": "孑",
"雎": "睢",
"汆": "氽",
"戍": "戌",
"妹": "妺",
"口": "囗",
"姬": "姫",
"祎": "袆",
"亳": "毫",
"汩": "汨",
"市": "巿",
"壸": "壶",
"祒": "袑",
"洗": "冼",
"夂": "夊",
"祖": "袓",
"芙": "褔",
"萬": "萭"
}

473
replacestr.py Normal file
View File

@ -0,0 +1,473 @@
import re
import random
import argparse
import sys
import os
from typing import List, Tuple, Optional, Dict, Any
from pathlib import Path
import logging
class TextProcessor:
"""文本处理器类,支持句子拆分和字符交换"""
def __init__(self, min_length: int = 30, custom_punctuation: Optional[str] = None):
"""
初始化文本处理器
Args:
min_length: 句子长度阈值
custom_punctuation: 自定义标点符号如果为None则使用默认标点
"""
self.min_length = min_length
self.sentence_endings = custom_punctuation or r'[,。!?;?!;]'
self.statistics = {
'total_sentences': 0,
'processed_sentences': 0,
'total_chars': 0,
'swapped_chars': 0
}
# 设置日志
logging.basicConfig(level=logging.INFO,
format='%(asctime)s - %(levelname)s - %(message)s')
self.logger = logging.getLogger(__name__)
def split_sentences(self, text: str) -> List[Tuple[str, str]]:
"""
按标点符号拆分句子保留标点符号
Args:
text: 输入文本
Returns:
List[Tuple[str, str]]: 每个元组包含 (句子内容, 标点符号)
"""
if not text.strip():
return []
# 使用正则表达式拆分,保留分隔符
parts = re.split(f'({self.sentence_endings})', text)
sentences = []
i = 0
while i < len(parts):
content = parts[i].strip()
if content: # 非空内容
# 检查下一个部分是否是标点符号
if i + 1 < len(parts) and re.match(self.sentence_endings, parts[i + 1]):
punctuation = parts[i + 1]
i += 2
else:
punctuation = ''
i += 1
sentences.append((content, punctuation))
self.statistics['total_sentences'] += 1
else:
i += 1
return sentences
def swap_random_chars(self, sentence: str) -> str:
"""
对超长句子随机交换相邻两个字符的顺序
Args:
sentence: 输入句子
Returns:
str: 处理后的句子
"""
# 边界情况处理
if not sentence or len(sentence) <= self.min_length or len(sentence) <= 3:
return sentence
# 转换为字符列表便于操作
chars = list(sentence)
original_length = len(chars)
# 确定可交换的范围(避开首尾字符,且需要成对相邻)
# 对于长度为n的句子可交换的相邻对位置为(1,2), (2,3), ..., (n-3,n-2)
start_idx = 1
end_idx = len(chars) - 3 # 最后一个可交换对的起始位置
if end_idx < start_idx:
return sentence
try:
# 随机选择一个相邻对的起始位置
swap_start = random.randint(start_idx, end_idx)
swap_end = swap_start + 1
# 交换相邻的两个字符
chars[swap_start], chars[swap_end] = chars[swap_end], chars[swap_start]
# 更新统计信息
self.statistics['processed_sentences'] += 1
self.statistics['swapped_chars'] += 2
self.logger.debug(f"交换相邻位置 {swap_start}{swap_end},句子长度:{original_length}")
except (ValueError, IndexError) as e:
self.logger.warning(f"字符交换失败:{e}")
return sentence
return ''.join(chars)
def process_text(self, text: str) -> str:
"""
处理文本拆分句子并对超长句子进行字符交换
Args:
text: 输入文本
Returns:
str: 处理后的文本
"""
if not text:
return text
# 重置统计信息
self.statistics = {
'total_sentences': 0,
'processed_sentences': 0,
'total_chars': len(text),
'swapped_chars': 0
}
# 按段落分割
paragraphs = text.split('\n')
processed_paragraphs = []
for paragraph in paragraphs:
if not paragraph.strip():
processed_paragraphs.append(paragraph)
continue
# 拆分句子
sentences = self.split_sentences(paragraph)
# 处理每个句子
processed_sentences = []
for sentence_content, punctuation in sentences:
# 对句子内容进行字符交换
processed_content = self.swap_random_chars(sentence_content)
processed_sentences.append(processed_content + punctuation)
# 重新组合句子
processed_paragraph = ''.join(processed_sentences)
processed_paragraphs.append(processed_paragraph)
return '\n'.join(processed_paragraphs)
def get_statistics(self) -> Dict[str, Any]:
"""获取处理统计信息"""
return self.statistics.copy()
def print_statistics(self):
"""打印处理统计信息"""
stats = self.get_statistics()
print("\n" + "=" * 50)
print("处理统计信息:")
print(f"总字符数:{stats['total_chars']}")
print(f"总句子数:{stats['total_sentences']}")
print(f"处理句子数:{stats['processed_sentences']}")
print(f"交换字符数:{stats['swapped_chars']}")
if stats['total_sentences'] > 0:
print(f"处理率:{stats['processed_sentences'] / stats['total_sentences'] * 100:.1f}%")
print("=" * 50)
class FileHandler:
"""文件处理器,负责文件的读写操作"""
@staticmethod
def read_file(filename: str) -> str:
"""
读取文件内容支持多种编码
Args:
filename: 文件路径
Returns:
str: 文件内容
Raises:
FileNotFoundError: 文件不存在
PermissionError: 权限不足
UnicodeDecodeError: 编码错误
"""
if not os.path.exists(filename):
raise FileNotFoundError(f"文件 '{filename}' 不存在")
if not os.access(filename, os.R_OK):
raise PermissionError(f"没有读取文件 '{filename}' 的权限")
# 尝试多种编码格式
encodings = ['utf-8', 'gbk', 'gb2312', 'latin-1']
for encoding in encodings:
try:
with open(filename, 'r', encoding=encoding) as f:
content = f.read()
logging.info(f"使用 {encoding} 编码成功读取文件:{filename}")
return content
except UnicodeDecodeError:
continue
raise UnicodeDecodeError(f"无法解码文件 '{filename}',尝试的编码格式:{encodings}")
@staticmethod
def write_file(filename: str, content: str, encoding: str = 'utf-8') -> None:
"""
写入文件内容
Args:
filename: 输出文件路径
content: 要写入的内容
encoding: 编码格式
Raises:
PermissionError: 权限不足
OSError: 磁盘空间不足等系统错误
"""
# 确保目录存在
output_dir = os.path.dirname(filename)
if output_dir and not os.path.exists(output_dir):
os.makedirs(output_dir, exist_ok=True)
try:
with open(filename, 'w', encoding=encoding) as f:
f.write(content)
logging.info(f"成功写入文件:{filename}")
except PermissionError:
raise PermissionError(f"没有写入文件 '{filename}' 的权限")
except OSError as e:
raise OSError(f"写入文件 '{filename}' 时发生错误:{e}")
def setup_argument_parser() -> argparse.ArgumentParser:
"""设置命令行参数解析器"""
parser = argparse.ArgumentParser(
description='文本句子字符交换处理器',
formatter_class=argparse.RawDescriptionHelpFormatter,
epilog="""
使用示例
%(prog)s -f input.txt # 处理文件
%(prog)s -t "你的文本内容" # 直接处理文本
%(prog)s -f input.txt -l 20 # 设置长度阈值为20
%(prog)s -f input.txt -o output.txt # 输出到文件
%(prog)s -f input.txt -p "。!?" -s # 自定义标点符号并显示统计
"""
)
# 输入选项
input_group = parser.add_mutually_exclusive_group(required=True)
input_group.add_argument('-f', '--file', help='输入文件路径')
input_group.add_argument('-t', '--text', help='直接输入文本')
input_group.add_argument('--stdin', action='store_true',
help='从标准输入读取文本')
# 处理选项
parser.add_argument('-l', '--length', type=int, default=30,
help='句子长度阈值默认30')
parser.add_argument('-p', '--punctuation',
help='自定义标点符号(默认:。!?;?!;')
parser.add_argument('-o', '--output', help='输出文件路径')
parser.add_argument('-e', '--encoding', default='utf-8',
help='输出文件编码默认utf-8')
# 其他选项
parser.add_argument('-s', '--statistics', action='store_true',
help='显示处理统计信息')
parser.add_argument('-v', '--verbose', action='store_true',
help='显示详细日志')
parser.add_argument('--seed', type=int, help='随机数种子(用于测试)')
return parser
def main():
"""主函数:处理命令行参数和文本处理"""
parser = setup_argument_parser()
args = parser.parse_args()
# 设置日志级别
if args.verbose:
logging.getLogger().setLevel(logging.DEBUG)
# 设置随机数种子(用于测试)
if args.seed:
random.seed(args.seed)
# 获取输入文本
try:
if args.file:
text = FileHandler.read_file(args.file)
elif args.text:
text = args.text
elif args.stdin:
text = sys.stdin.read()
else:
print("错误:请指定输入源")
sys.exit(1)
if not text.strip():
print("警告:输入文本为空")
sys.exit(0)
except (FileNotFoundError, PermissionError, UnicodeDecodeError) as e:
print(f"错误:{e}")
sys.exit(1)
# 创建处理器并处理文本
try:
processor = TextProcessor(
min_length=args.length,
custom_punctuation=args.punctuation
)
processed_text = processor.process_text(text)
# 输出结果
if args.output:
FileHandler.write_file(args.output, processed_text, args.encoding)
print(f"处理完成,结果已保存到 '{args.output}'")
else:
print("处理结果:")
print("-" * 50)
print(processed_text)
# 显示统计信息
if args.statistics:
processor.print_statistics()
except Exception as e:
print(f"处理过程中发生错误:{e}")
if args.verbose:
import traceback
traceback.print_exc()
sys.exit(1)
# 单元测试
def run_tests():
"""运行基本的单元测试"""
print("运行单元测试...")
# 测试句子拆分
processor = TextProcessor(min_length=6)
# 测试1普通句子拆分
test_text = "这是第一句。这是第二句!第三句?"
sentences = processor.split_sentences(test_text)
assert len(sentences) == 3, f"期望3个句子实际{len(sentences)}"
assert sentences[0] == ("这是第一句", ""), f"第一句解析错误:{sentences[0]}"
# 测试2相邻字符交换
long_sentence = "这是一个很长的句子用来测试字符交换功能"
random.seed(42) # 固定种子以便测试
result = processor.swap_random_chars(long_sentence)
assert result != long_sentence, "长句子应该被修改"
assert len(result) == len(long_sentence), "交换后长度应该不变"
# 验证只交换了相邻的两个字符
diff_count = sum(1 for i, (a, b) in enumerate(zip(long_sentence, result)) if a != b)
assert diff_count == 2, f"应该只有2个字符位置发生变化实际{diff_count}"
# 测试3短句子不变
short_sentence = "短句"
result = processor.swap_random_chars(short_sentence)
assert result == short_sentence, "短句子不应该被修改"
# 测试4边界情况
empty_result = processor.swap_random_chars("")
assert empty_result == "", "空字符串应该保持不变"
print("✓ 所有测试通过!")
# 示例使用
def replace_text(text):
# 检查是否运行测试
if len(sys.argv) > 1 and sys.argv[1] == 'test':
run_tests()
sys.exit(0)
# 命令行模式
if len(sys.argv) > 1:
main()
else:
# 示例演示
sample_text = text
print("示例演示:")
print("原文:")
print(sample_text)
print("\n" + "=" * 50 + "\n")
min_length = 12
processor = TextProcessor(min_length)
processed = processor.process_text(sample_text)
print("处理后:")
print(processed)
processor.print_statistics()
print("\n使用说明:")
print("命令行用法:")
print(" python script.py -f input.txt # 处理文件")
print(" python script.py -t '你的文本内容' # 直接处理文本")
print(" python script.py -f input.txt -l 20 # 设置长度阈值为20")
print(" python script.py -f input.txt -o output.txt # 输出到文件")
print(" python script.py -f input.txt -p '。!?' -s # 自定义标点符号并显示统计")
print(" python script.py test # 运行单元测试")
return processed
text = """阅读此文之前,麻烦您点击一下“关注”,既方便您进行讨论和分享,又能给您带来不一样的参与感,创作不易,感谢您的支持。
曾经半路出家如今黯然无声他的故事值得一品
说起央视的主持人大家第一反应肯定是一个个字正腔圆形象出彩的脸孔可是在这其中有一位却用浓厚的潮汕口音还有点油滑的幽默自成一派他就是阿丘
这个名字可能在现在已经鲜有人提起但在过去他可是实打实的名嘴不过咱来说点耐人寻味的他是怎么走到央视巅峰又怎么高台跳水这故事够扎心更够意味深长
看似格格不入却杀出重围
熟悉阿丘的人一听他那口音就知道这是岭南口音模块的标配他是个土生土长的广东人也因为家里是军人家庭小时候经常搬家成了个活脱脱的语言天才学会了好几个地方的方言什么潮汕话粤语客家话信手拈来不得不说小时候到处跑打下的基础倒给他多了一点和别人不一样的人味儿
他大学学的专业可和主持半毛钱关系没有是经济学毕业后他分配到了南宁的棉纺印染厂待遇不错是个政工干部这时候的阿丘怎么看都是个稳稳的职场小白可谁能想到后来的他能走上舞台呢
90年代相声小品各类幽默比赛风靡全国阿丘平时最爱的就是琢磨这些妙语段子一心觉得自己是个未被发现的宝藏男孩机会来了1992他参加广西举办的笑星大赛居然拿了个一等奖这下可出名了厂里人都认识他他本人也成了地方笑星
再后来他调到了广西电视台开始主持节目头几年波澜不惊直到他参加南北笑星火辣辣凭借风趣和机灵吸引了更多目光2003这个来自地方台的主持人直接杀进了央视主持圈靠什么靠他的个性和风格
从风光无限到画风突变
阿丘进入央视后主持了好几档节目他的幽默和接地气与当时一板一眼的正规主持人大不相同因此他迅速被贴上个性主持的标签尤其是在社会记录那带点潮汕腔调的问句竟成了一种标志
可惜说话爽快的他也因为不当言论栽了跟头事情发生在2020年正是全国上下齐心合力抗击疫情的时候阿丘不知道怎么回事在自己的博客里发了一些让人难以接受的言论里头什么东亚病夫道歉显得格外刺眼
不得不说这一锅凉水泼得够彻底网友立刻开始深挖一挖还真揭出不少黑历史有人爆料他婚内包养女大学生还试图给实习机会虽然阿丘本人否认得七七八八但这些传闻和再度破裂的婚姻难免让人联想
面对铺天盖地的指责阿丘的态度是硬得离谱一句道歉都没有嘴皮子在这时候完全失灵了要说在镜头前笑侃万事的大叔这一次是真没能站住脚
离开央视后的低调生活
最后阿丘与央视长达12年的缘分彻底告一段落此后的日子他也算是从公众视线中消失了最让人记得的是他两年后现身老搭档张泉灵的节目只是这一次他的亮相显得缥缈又散淡
如今阿丘的身份更多转向了自媒体开了个叫阿丘观山的账号做起旅游文化博主视频里他介绍名山大川什么五台山武当山天天讲人生感悟这画风和过去主持访谈节目的他可真是差太远了
不少老观众打开他的账号可能都得感叹一声物是人非更有网友直言他的语气里听到了些许悔意又觉得是假装云淡风轻实际还是难以摆脱舆论的阴影
留下的启示和争议
阿丘的故事是难得一见的从地方电视台到央视舞台他用12年时间登上顶峰却因为12个字毁了前程这起伏真像一出大戏
咱们反思一下也许有些人天赋机遇都抓得很精准但言行失当永远是会砸场子的导火索阿丘的人生轨迹正说明了这一点
现在问题来了大家怎么看阿丘这个人你是觉得他个性可惜还是自毁前程
欢迎留言讨论你们的每一次互动都是创作的动力"""
result = replace_text(text)
print(result)