C语言实现类*NIX cp工具:MP4/MP3复制损坏问题求助
Let's break down why your tool works for text files but breaks MP4/MP3s—this is a classic pitfall when dealing with binary vs text modes in C, plus some easy-to-miss pipe and buffer handling details.
The Root Cause(s)
You're using text mode for file I/O
When you open a file withfopen("path", "r")or"w", C uses text mode. On *NIX systems this is mostly harmless, but it still treats certain bytes (like0x1A, the traditional EOF character) as a signal to stop reading. Binary files like MP4s are full of non-text bytes, so your code is likely truncating the file early when it hits one of these.You're writing the full buffer every time, even if you didn't read 250 bytes
If your loop reads fewer than 250 bytes (which happens at the end of the file), writing the entire buffer will append garbage data to the end of your target file—this corrupts the file's header or metadata, which is why media players can't recognize it.Incorrect pipe file descriptor management
If you don't close unused pipe ends in parent/child processes, your read calls might hang waiting for EOF, leaving the target file incomplete.
Step-by-Step Fixes
1. Switch to binary mode for file operations
Replace your fopen calls with binary mode flags:
// Open source file in binary read mode FILE *src_fp = fopen(argv[1], "rb"); if (src_fp == NULL) { perror("Failed to open source file"); exit(1); } // Open target file in binary write mode FILE *dst_fp = fopen(argv[2], "wb"); if (dst_fp == NULL) { perror("Failed to open target file"); exit(1); }
This tells C to read/write every byte exactly as it is, no translations or early termination.
2. Respect the actual number of bytes read
Instead of assuming you always fill the 250-byte buffer, capture the return value of fread (or read for system calls) and write only that many bytes:
char buf[250]; size_t bytes_read; // Read from source, write to pipe (parent process example) while ((bytes_read = fread(buf, 1, sizeof(buf), src_fp)) > 0) { if (write(pipefd[1], buf, bytes_read) != bytes_read) { perror("Failed to write to pipe"); exit(1); } } // In the child process: read from pipe, write to target while ((bytes_read = read(pipefd[0], buf, sizeof(buf))) > 0) { if (fwrite(buf, 1, bytes_read, dst_fp) != bytes_read) { perror("Failed to write to target file"); exit(1); } }
This ensures you never write extra garbage data, and you handle the final partial buffer correctly.
3. Clean up pipe file descriptors
After forking, make sure each process closes the pipe ends it doesn't use. For example:
int pipefd[2]; if (pipe(pipefd) == -1) { perror("Pipe failed"); exit(1); } pid_t pid = fork(); if (pid == 0) { // Child: reads from pipe, writes to target close(pipefd[1]); // Close unused write end // ... read/write logic ... close(pipefd[0]); // Clean up read end when done exit(0); } else { // Parent: reads from source, writes to pipe close(pipefd[0]); // Close unused read end // ... read/write logic ... close(pipefd[1]); // Signal EOF to child by closing write end wait(NULL); // Wait for child to finish }
Closing unused ends ensures that when the parent finishes writing, the child's read will get EOF and exit properly—no hanging, no incomplete files.
Verify the Fix
After making these changes, compare the size of the source and target files with ls -l. They should be identical. If they are, your media files should open correctly.
内容的提问来源于stack exchange,提问作者Kushagr Tyagi

