好友
阅读权限30
听众
最后登录1970-1-1
|
本帖最后由 onlyclxy 于 2024-9-25 18:32 编辑
这个问题已经解决.写作学习记录.
是python有个缓冲机制,缓冲是一种将输出暂时存储在内存中,然后批量写入到文件或屏幕的技术。当Python程序的输出被重定向到文件或管道时,通常会启用缓冲以提高性能,但这可能会导致输出的延迟或顺序混乱。
在Python中,-u选项用于在输出中禁用缓冲。使用-u选项可以禁用这种缓冲机制,即实时地将输出写入到文件或屏幕,从而确保输出立即显示。
经测试 python -u xxx.py 这个可以在c#实时读取
pyinstaller说明里也有个-u 说是可以生成无缓冲文件. pyinstaller --onefile -u your_script.py
同时还有个写法
[Python] 纯文本查看 复制代码 import sys
import time
print("Starting...", flush=True) # 添加 flush=True
while True:
print("Running...", flush=True)
time.sleep(1)
还有可以设置全局变量 set PYTHONUNBUFFERED=1 这个经测试对于python运行的有效 对于已经生成的exe无效
但是虽然说可以生成无缓冲的文件.但是对于别人的exe,还是做不到实时读取
然后又去问了群里的资深球佬.
球佬给我讲了个故事. 说以前有个群友需要用ssh做远程连接. 但是ssh那个需要输入一个密码. 那个密码不能说通过传参数的形式去传过去.只能是在那个框框去输入.
说你看包括vscode 或者 ide 里面都有那种框框. 那种你无论拖入什么程序,.他都可以去打印里面的应该打印的信息. 他那个东西叫伪终端.
他说他自己在unix上就搞了一套伪终端, 用这个伪终端解决了很多问题.
然后我很激动嘛 就去查这个伪终端. 查到一个WinPty的终端模拟器
就去网上搜, 搜到的项目貌似还需要我自己编译.. 我就去问gpt这个不是exe吗 ? gpt说你去下个git git里有.我寻思我有啊 一搜本地还真有
没想到这个winpry不光git有. vscode有,vsstudio有.anaconda里有.. 原来这个这么通用啊.
然后就尝试找c#去怎使用这个winpty.
最后结果发现我自己还是不太会弄这个winpty. 然后发现c#的库里有个现成的库winpty.Net
这个库我看解决了上面那个缓冲问题. 弄上后. 成功可以在c#输出python的实时消息了
我观察任务管理器里,他这个和那些一样. 会挂一个命令提示符,和打开的python文件
不过现在仍然有些小问题 就是framework下, 打印的字会有乱码. 我猜测可能是\r\n这种东西变成了乱码. 但这个乱码同样的代码复制到.net6就不存在了.暂时不清楚原理.
为了能在framework用..暂时只能用替换的方法.把那些乱码替换掉了. 但是不确定是否还会有其他乱码. 如果有了解这块的大佬还请指教.
下面贴一下现在能跑的c#代码.
[C#] 纯文本查看 复制代码 using System;
using System.IO;
using System.IO.Pipes;
using static winpty.WinPty;
class WinPtyExample
{
static int Main(string[] args)
{
IntPtr handle = IntPtr.Zero;
IntPtr err = IntPtr.Zero;
IntPtr cfg = IntPtr.Zero;
IntPtr spawnCfg = IntPtr.Zero;
Stream stdin = null;
Stream stdout = null;
try
{
// 创建 WinPTY 配置
cfg = winpty_config_new(WINPTY_FLAG_COLOR_ESCAPES, out err);
winpty_config_set_initial_size(cfg, 80, 32);
handle = winpty_open(cfg, out err);
if (err != IntPtr.Zero)
{
Console.WriteLine($"Error opening WinPTY: {winpty_error_code(err)}");
return 1;
}
// 指定要运行的 exe(cmd.exe)和参数(为空字符串)
string exe = @"C:\Windows\System32\cmd.exe";
string exeArgs = ""; // 无参数
string cwd = @"C:\"; // 工作目录
// 创建进程配置
spawnCfg = winpty_spawn_config_new(WINPTY_SPAWN_FLAG_AUTO_SHUTDOWN, exe, exeArgs, cwd, null, out err);
if (err != IntPtr.Zero)
{
Console.WriteLine($"Error creating spawn config: {winpty_error_code(err)}");
return 1;
}
// 创建输入和输出管道
stdin = CreatePipe(winpty_conin_name(handle), PipeDirection.Out);
stdout = CreatePipe(winpty_conout_name(handle), PipeDirection.In);
// 启动进程
if (!winpty_spawn(handle, spawnCfg, out IntPtr process, out IntPtr thread, out int procError, out err))
{
Console.WriteLine($"Spawn failed with error code: {procError}"); // 输出错误代码
Console.WriteLine($"Error during spawn: {winpty_error_code(err)}");
return 1;
}
// 读取输出
using (var reader = new StreamReader(stdout))
{
string outputLine;
while ((outputLine = reader.ReadLine()) != null)
{
Console.WriteLine(outputLine);
}
}
return 0;
}
finally
{
stdin?.Dispose();
stdout?.Dispose();
winpty_config_free(cfg);
winpty_spawn_config_free(spawnCfg);
winpty_error_free(err);
winpty_free(handle);
}
}
private static Stream CreatePipe(string pipeName, PipeDirection direction)
{
string serverName = ".";
if (pipeName.StartsWith("\\"))
{
int slash3 = pipeName.IndexOf('\\', 2);
if (slash3 != -1)
{
serverName = pipeName.Substring(2, slash3 - 2);
}
int slash4 = pipeName.IndexOf('\\', slash3 + 1);
if (slash4 != -1)
{
pipeName = pipeName.Substring(slash4 + 1);
}
}
var pipe = new NamedPipeClientStream(serverName, pipeName, direction);
pipe.Connect();
return pipe;
}
}
下面是暂时的成品界面版的.
[C#] 纯文本查看 复制代码 using System;
using System.IO;
using System.IO.Pipes;
using System.Text;
using System.Text.RegularExpressions;
using System.Threading.Tasks;
using System.Windows.Forms;
using static winpty.WinPty;
namespace WindowsFormsApp1
{
public partial class Form1 : Form
{
private TextBox outputTextBox;
private Button runButton;
public Form1()
{
InitializeComponent();
InitializeControls();
}
private void InitializeControls()
{
outputTextBox = new TextBox
{
Multiline = true,
Dock = DockStyle.Fill,
ScrollBars = ScrollBars.Both,
Font = new System.Drawing.Font("Consolas", 10)
};
this.Controls.Add(outputTextBox);
runButton = new Button
{
Text = "运行命令",
Dock = DockStyle.Top
};
runButton.Click += runButton_Click;
this.Controls.Add(runButton);
this.Text = "WinPty 示例";
this.Size = new System.Drawing.Size(800, 600);
}
private async void runButton_Click(object sender, EventArgs e)
{
outputTextBox.Clear();
await Task.Run(() => RunExecutable());
}
private void RunExecutable()
{
IntPtr handle = IntPtr.Zero;
IntPtr err = IntPtr.Zero;
IntPtr cfg = IntPtr.Zero;
IntPtr spawnCfg = IntPtr.Zero;
Stream stdin = null;
Stream stdout = null;
try
{
cfg = winpty_config_new(WINPTY_FLAG_COLOR_ESCAPES, out err);
winpty_config_set_initial_size(cfg, 80, 32);
handle = winpty_open(cfg, out err);
if (err != IntPtr.Zero)
{
UpdateOutput($"Error opening WinPTY: {winpty_error_code(err)}");
return;
}
string exe = @"C:\Windows\System32\cmd.exe";
//exe = @"C:\死循环测试.exe";
string exeArgs = "";
string cwd = @"C:\";
spawnCfg = winpty_spawn_config_new(WINPTY_SPAWN_FLAG_AUTO_SHUTDOWN, exe, exeArgs, cwd, null, out err);
if (err != IntPtr.Zero)
{
UpdateOutput($"Error creating spawn config: {winpty_error_code(err)}");
return;
}
stdin = CreatePipe(winpty_conin_name(handle), PipeDirection.Out);
stdout = CreatePipe(winpty_conout_name(handle), PipeDirection.In);
if (!winpty_spawn(handle, spawnCfg, out IntPtr process, out IntPtr thread, out int procError, out err))
{
UpdateOutput($"Spawn failed with error code: {procError}");
UpdateOutput($"Error during spawn: {winpty_error_code(err)}");
return;
}
using (var reader = new StreamReader(stdout, Encoding.UTF8))
{
string outputLine;
while ((outputLine = reader.ReadLine()) != null)
{
//string cleanOutput = RemoveSpecialCharacters(outputLine);
//cleanOutput = RemoveSpecificCharacters(cleanOutput);
outputLine = CleanUp(outputLine);
UpdateOutput(outputLine);
}
}
}
finally
{
stdin?.Dispose();
stdout?.Dispose();
winpty_config_free(cfg);
winpty_spawn_config_free(spawnCfg);
winpty_error_free(err);
winpty_free(handle);
}
}
private string RemoveSpecialCharacters(string input)
{
// 过滤掉 ANSI 转义序列和其他特定控制字符
string pattern = @"(\x1B\[[0-?9;]*[mG]|(\x1B\]0;.*?\x07)|[\x07])";
string cleanedInput = Regex.Replace(input, pattern, string.Empty);
// 过滤掉特殊字符、ANSI 转义序列和控制字符
pattern = @"(\x1B\[[0-?9;]*[mK]|[\x07])"; // 匹配 ANSI 转义序列
cleanedInput = Regex.Replace(cleanedInput, pattern, string.Empty);
// 去掉行首和行尾的空白字符
return cleanedInput.Trim();
}
public static string CleanUp(string input)
{
// 移除第一组字符
input = input.Replace("\x1B[0m", ""); // 删除字符: (ASCII: 27) + [ + 0 + m
input = input.Replace("\x1B[0K", ""); // 删除字符: (ASCII: 27) + [ + 0 + K
// 移除第二组字符
input = input.Replace("\x1B[?25l", ""); // 删除字符: (ASCII: 27) + [ + ? + 2 + 5 + l
// 移除第三组字符
input = input.Replace("\x1B[?25h", ""); // 删除字符: (ASCII: 27) + [ + ? + 2 + 5 + h
// 移除第四组字符
input = input.Replace("\x1B]0;", ""); // 删除字符: (ASCII: 27) + ] + 0 + ;
// 移除第五组字符
input = input.Replace("\a", ""); // 删除字符: (ASCII: 7)
input = input.Replace("\x1B[0m", ""); // 删除字符: (ASCII: 27) + [ + 0 + m
input = input.Replace("\r\n", "\n") // 统一换行符
.Replace("\n\n", "\n"); // 删除连续空行
while (input.Contains("\n\n")) // 循环直到没有连续空行
{
input = input.Replace("\n\n", "\n");
}
if (input.StartsWith("\n"))
{
input = input.Substring(1); // 删除开头的换行符
}
return input;
}
private void UpdateOutput(string message)
{
if (outputTextBox.InvokeRequired)
{
outputTextBox.Invoke(new Action<string>(UpdateOutput), message);
}
else
{
outputTextBox.AppendText(message + Environment.NewLine);
}
}
private Stream CreatePipe(string pipeName, PipeDirection direction)
{
string serverName = ".";
if (pipeName.StartsWith("\\"))
{
int slash3 = pipeName.IndexOf('\\', 2);
if (slash3 != -1)
{
serverName = pipeName.Substring(2, slash3 - 2);
}
int slash4 = pipeName.IndexOf('\\', slash3 + 1);
if (slash4 != -1)
{
pipeName = pipeName.Substring(slash4 + 1);
}
}
var pipe = new NamedPipeClientStream(serverName, pipeName, direction);
pipe.Connect();
return pipe;
}
}
}
求助各位大佬 有点想不通
本来那种命令行的exe.可以通过cmd打开.
我想着用c#写一个, 也是获取这种命令行的exe 然后输出到编辑框里
结果写到一半发现这个c#无法获取我用python写的一个东西,打印都打印不出来
python是写了一个死循环,生成了个exe
[Python] 纯文本查看 复制代码 import time
def main():
start_time = time.time() # 获取脚本开始运行的时间
while True:
elapsed_time = time.time() - start_time # 计算已过时间
print(f"这是一个测试循环,已运行 {int(elapsed_time)} 秒")
time.sleep(1) # 暂停一秒
if __name__ == "__main__":
main()
然后c#这边写了个异步读取.现在连打印都不打印这个,就卡在第一行里
[Java] 纯文本查看 复制代码 using System;
using System.Diagnostics;
using System.Threading.Tasks;
class Program
{
static async Task Main(string[] args)
{
// 输入 exe 文件的路径
Console.WriteLine("请输入 exe 文件的路径:");
string exePath = Console.ReadLine();
// 检查路径是否为空或无效
if (string.IsNullOrWhiteSpace(exePath))
{
Console.WriteLine("请输入有效的路径。");
return;
}
try
{
// 异步捕获并输出
await RunProcessAsync(exePath);
}
catch (Exception ex)
{
Console.WriteLine($"运行过程中发生错误: {ex.Message}");
}
}
static async Task RunProcessAsync(string exePath)
{
var process = new Process
{
StartInfo = new ProcessStartInfo
{
FileName = exePath, // EXE 路径
RedirectStandardOutput = true, // 重定向标准输出
RedirectStandardError = true, // 重定向标准错误
UseShellExecute = false, // 必须禁用 Shell 执行以重定向流
CreateNoWindow = true // 禁止创建窗口
},
EnableRaisingEvents = true // 启用事件以便异步通知
};
// 当输出时触发事件
process.OutputDataReceived += (sender, e) =>
{
if (!string.IsNullOrEmpty(e.Data))
{
Console.WriteLine(e.Data); // 打印标准输出的每一行
}
};
// 当错误输出时触发事件
process.ErrorDataReceived += (sender, e) =>
{
if (!string.IsNullOrEmpty(e.Data))
{
Console.Error.WriteLine(e.Data); // 打印标准错误的每一行
}
};
// 异步启动进程
process.Start();
// 异步读取输出和错误流
process.BeginOutputReadLine();
process.BeginErrorReadLine();
// 等待进程完成
await process.WaitForExitAsync();
Console.WriteLine($"进程结束,退出代码: {process.ExitCode}");
}
}
界面的:
[C#] 纯文本查看 复制代码 using System;
using System.Diagnostics;
using System.IO;
using System.Windows.Forms;
using System.Runtime.InteropServices;
using System.Threading.Tasks;
namespace 命令行界面
{
public partial class Form1 : Form
{
private string programPath = "C:\\死循环测试.exe"; // 这里假设程序路径已固定,您可以根据实际情况修改
public Form1()
{
InitializeComponent();
}
private async Task button1_Click(object sender, EventArgs e)
{
await ExecuteCommandAsync(programPath);
}
private async Task ExecuteCommandAsync(string command)
{
// 设置为启动cmd.exe,并通过参数传递要执行的命令
ProcessStartInfo startInfo = new ProcessStartInfo
{
FileName = "cmd.exe", // 使用cmd.exe作为启动程序
Arguments = $"/c {command}", // /c 表示执行字符串指定的命令然后结束
UseShellExecute = false,
RedirectStandardOutput = true,
RedirectStandardError = true, // 同时捕获错误输出
CreateNoWindow = true
};
using (Process process = new Process { StartInfo = startInfo })
{
process.Start();
// 异步读取标准输出
while (!process.StandardOutput.EndOfStream)
{
string line = await process.StandardOutput.ReadLineAsync();
UpdateTextBox(line);
}
// 异步读取标准错误输出
while (!process.StandardError.EndOfStream)
{
string errorLine = await process.StandardError.ReadLineAsync();
UpdateTextBox("Error: " + errorLine); // 可以区分错误信息
}
}
}
private void UpdateTextBox(string text)
{
if (textBoxOutput.InvokeRequired)
{
textBoxOutput.Invoke(new MethodInvoker(delegate
{
textBoxOutput.AppendText(text + Environment.NewLine);
}));
}
else
{
textBoxOutput.AppendText(text + Environment.NewLine);
}
}
}
}
我感觉不应该啊 这种东西实现不了吗 gpt出了一个晚上也没搞定.
求助各位大佬 |
|